2009-03-12 8 views
0

Cela peut avoir été posée, mais il est vraiment difficile de rechercher des termes qui limitent les résultats de recherche ...Sql Server INSERT problème de portée

Prenez l'extrait de code SQL suivant:

declare @source table (id int) 
declare @target table(id int primary key, sourceId int) 


set nocount on 

insert into @target values (0,0) 

insert into @source(id) values(1) 
--insert into @source(id) values(2) 

set nocount off 

insert into @target select (select max(id)+1 from @target), s.id from @source s 

select * from @target 

Cette évidence exécute sans erreur, mais maintenant décommenter la deuxième ligne d'insertion et l'erreur suivante se produit:

Msg 2627, Level 14, State 1, Line 15 
Violation of PRIMARY KEY constraint 'PK__#7DB3CB72__7EA7EFAB'. Cannot insert duplicate key in object '[email protected]'. 

Je me rends compte que l'instruction d'insertion plus est probablement effectué contre un o instantané f la table @target (select max(id)+1 from @target) retournera toujours une valeur de 1 - provoquant l'erreur de violation ci-dessus ...

Y a-t-il un moyen de contourner cela en dehors du recours au curseur?

Répondre

1

Changer votre instruction d'insertion à ce qui suit:

insertion dans @Target sélectionnez (sélectionnez max (id) de @Target) + (ROW_NUMBER() OVER (ORDER BY s.id)), à partir de @source s

Cela devrait fonctionner dans ce cas précis, mais je ferais attention de le généraliser.

+0

+1. @Sean Si vous ne pouvez pas modifier la cible pour utiliser l'identité, cette solution devrait fonctionner pour vous. Assurez-vous simplement de ne pas exécuter plusieurs instructions en même temps. – kristof

+0

@Brian - Si j'avais assez de réputation, je donnerais +1 à votre réponse. C'est précisément le type de réponse que j'espérais. Merci. – Sean

1

Vous pouvez utiliser une colonne d'identité (qui est exactement ce qu'ils sont destinés à)

declare @target table(id int IDENTITY(1,1), sourceId int) 

Si votre problème est que la clause select est « calculée » avant l'insert est exécuté, il n'y a afaik pas moyen de contourner cette en utilisant une seule requête SQL

Je pense que c'est par conception; Pour que votre insertion évite les doublons, l'identifiant de l'index doit être calculé pendant l'insertion, pas pendant la sélection. C'est l'objet exact du mot-clé IDENTITY.

Si vous souhaitez insérer une sélection à la fois, vous devez écrire des demandes distinctes (à l'aide des curseurs par exemple, mais vous perdrez atomicité, et vous devrez utiliser des mots clés de verrouillage appropriés pour éviter les conditions de course)

+0

Merci pour la réponse, bien que je cherche une réponse à ce scénario particulier car c'est ce que je fais face à l'heure actuelle. Je ne peux pas ajuster les structures de table et exige exactement ce qui est attendu de ce qui précède. Notez que la valeur de départ n'est pas forcément une, en fonction du contenu des cibles ... – Sean

+0

@Sean: alors vous devriez reformuler votre question afin que vous exposiez ce qu'est votre vrai problème, pas une simplification (ce qui conduit au genre de réponse simpliste que vous obtenez maintenant) – Brann

+0

@Sean: vous pouvez également vouloir fixer votre titre afin qu'il décrit plus précisément votre question. – Brann

0

La façon dont vous déterminez votre nouvelle valeur de PK est une condition de concurrence qui attend de se produire. Si votre base de données est sous forte charge et que plusieurs enregistrements sont insérés en même temps, vous obtiendrez des résultats inattendus. Pourquoi ne pas simplement utiliser une colonne d'identité, et laisser la base de données gérer l'assignation d'un nouvel identifiant primaire? Ou, vous pouvez créer une sorte de méta-table, qui contient un enregistrement pour chaque table de votre base de données, et cet enregistrement contient la valeur suivante qui doit être utilisée comme ID principal dans la table. Ensuite, vous devez vous assurer que chaque fois que vous créez un nouvel enregistrement, vous mettez également à jour la valeur suivante dans votre méta-table (et vous devez vous assurer que vous faites le verrouillage approprié), mais je ne vois aucune valeur ajoutée cette approche vs l'utilisation de colonnes d'identité.

+0

Voir le commentaire ci-dessus. Ne vous inquiétez pas des conditions de course - cela doit être fait dans un environnement très contrôlé. Et le PK est une clé composée - l'échantillon ci-dessus est juste cela - un échantillon ... il ne représente pas le scénario entier - juste la section du scénario me causant un problème. Merci quand même. – Sean