2010-12-02 37 views
4

J'utilise une instruction MERGE pour faire un UPSERT. Pour les lignes qui ne sont pas présentes dans la destination, je voudrais les insérer dans un certain ordre. Malheureusement, il semble que la clause ORDER BY n'est pas prise en charge avec une instruction de fusion. Y at-il un moyen de le faire dans une déclaration? Voir par exemple pour une meilleure idée de ce que je suis en train de faire:Pouvez-vous spécifier un rang sans correspondance d'ordre dans une instruction MERGE SQL Server 2008?

CREATE TABLE #destination (ident int not null identity(1,1), id int not null, value int not null) 
INSERT INTO #destination (id,value) VALUES (1,50) 

CREATE TABLE #source (id int not null, value int not null) 
INSERT INTO #source (id,value) VALUES (1,100),(3,300),(2,200) 

MERGE #destination d 
USING #source s 
    ON d.id = s.id 
WHEN MATCHED THEN 
    UPDATE 
    SET d.value = s.value 
WHEN NOT MATCHED THEN 
    INSERT (id,value) 
    VALUES (s.id,s.value); 

SELECT * FROM #destination ORDER BY ident 
/* 
WILL LIKELY SEE: 
    1, 1, 100 
    2, 3, 300 
    3, 2, 200 
WANT TO ACHIEVE: 
    1, 1, 100 
    2, 2, 200 
    3, 3, 300 
*/ 

La raison pour laquelle je veux faire est que je voudrais écrire un test unitaire pour mon code qui effectue cette fusion et que vous souhaitez les insertions dans un déterministe commande. Je sais qu'il y a des moyens de contourner cela, mais s'il y a un moyen d'ordonner l'insertion d'un MERGE, ce serait le plus facile.

Répondre

2

Donc maintenant ce n'est pas possible. Depuis le documentation on MERGE, l'opération des insertions, suppressions et mises à jour est non ordonnée; mais il a été inhumé dans son explication de la façon dont TOP l'affecte:

La clause TOP réduit encore le nombre de lignes jointes à la valeur spécifique et l'insert, mise à jour ou supprimer les actions sont appliquées à la les rangées jointes restantes dans une mode non ordonnée . Autrement dit, il n'y a pas d'ordre dans lequel les lignes sont réparties entre les actions définies dans les clauses WHEN . Par exemple, la spécification de TOP (10) affecte 10 lignes; de ces lignes, 7 peut être mis à jour et 3 inséré, ou 1 peuvent être supprimés, 5 mis à jour, et 4 inséré et ainsi de suite.

+0

Votre citation indique clairement qu'il n'y a pas d'ordre de lignes mais je m'interroge sur les opérations. Au moins dans les deux exemples, la mise à jour est antérieure à l'insertion. Donc pourrait-il être que les opérations sont effectuées dans le même ordre? Supprimer, mettre à jour, puis insérer? Y a-t-il un moyen d'influencer cela? –

1

Vous ne savez pas si cela serait acceptable dans votre cas, mais vous pouvez utiliser SET IDENTITY_INSERT pour remplacer la colonne d'identité et garantir votre commande de cette façon.

CREATE TABLE #destination (ident int not null identity(1,1), id int not null, value int not null) 
INSERT INTO #destination (id,value) VALUES (1,50) 

CREATE TABLE #source (id int not null, value int not null) 
INSERT INTO #source (id,value) VALUES (1,100),(3,300),(2,200) 

SET IDENTITY_INSERT #destination ON 

MERGE #destination d 
USING #source s 
    ON d.id = s.id 
WHEN MATCHED THEN 
    UPDATE 
    SET d.value = s.value 
WHEN NOT MATCHED THEN 
    INSERT (ident,id,value) 
    VALUES (s.id, s.id, s.value); 

SET IDENTITY_INSERT #destination OFF 

SELECT * FROM #destination ORDER BY ident 
+0

Je pense que cela fonctionne dans ce cas parce que mon exemple est super simple avec ident égal à id. Mais si je veux une colonne d'identité qui soit séquentielle, mais qui ne soit pas garantie de colonnes d'identification égales ou séquentielles, alors elle se décompose. En d'autres termes, toute la raison pour laquelle je veux commander est que je n'ai pas à m'inquiéter de la colonne d'identité – Zugwalt