2016-05-16 2 views
0

navigation sur divers exemples sur la façon de créer une déclaration de UPSERT « bonne » shown here, j'ai créé le code suivant (j'ai changé les noms de colonnes):SQL Server: transaction UPSERT ne pas atteindre insérer

BEGIN TRANSACTION 

IF EXISTS (SELECT * 
      FROM Table1 WITH (UPDLOCK, SERIALIZABLE), Table2 
      WHERE Table1.Data3 = Table2.data3) 
BEGIN 
    UPDATE Table1 
    SET Table1.someColumn = Table2.someColumn, 
     Table1.DateData2 = GETDATE() 
    FROM Table1 
    INNER JOIN Table2 ON Table1.Data3 = Table2.data3 
END 
ELSE 
BEGIN 
    INSERT INTO Table1 (DataComment, Data1, Data2, Data3, Data4, DateData1, DateData2) 
     SELECT 
      'some comment', data1, data2, data3, data4, GETDATE(), GETDATE() 
     FROM 
      Table2 
END 

COMMIT TRANSACTION 

Mon le problème est, qu'il ne fait jamais la partie INSERT. Le INSERT seul fonctionne bien. Le script en cours ne fait que la partie de mise à jour.

J'ai une idée que l'insertion est seulement bonne, si elle peut insérer l'ensemble des données qu'elle trouve (à cause de la requête de sélection)? Sinon, cela ne fonctionnera pas. Si oui, comment puis-je l'améliorer?

J'ai également lu à propos de la clause MERGE et je voudrais l'éviter.

// EDIT:

Après avoir essayé quelques échantillons trouvés sur Internet et expliqué ici, je re-fait ma logique comme suit:

BEGIN TRANSACTION 
    BEGIN 
     UPDATE table1 
     SET something 
     WHERE condition is met 

     UPDATE table2 
     SET helpColumn = 'DONE' 
     WHERE condition is met 
    END 
    BEGIN 
     INSERT INTO table1(data) 
     SELECT data 
     FROM table2 
     WHERE helpColumn != 'DONE' 
    END 
COMMIT TRANSACTION 

Lorsque vous essayez d'autres solutions, la INSERT habituellement échoué ou a fonctionné pendant longtemps (sur quelques tables, je peux l'accepter, mais pas bon, si vous projetez de migrer des données entières d'une base de données à une autre base de données).

Ce n'est probablement pas la meilleure solution, je pense. Mais pour l'instant ça marche, des commentaires?

+1

Vous vérifiez actuellement si le EXISTS renvoie toutes les lignes, mais il n'y a pas de corrélation avec la mise à jour réelle. Pourquoi ne voulez-vous pas utiliser MERGE, c'est exactement pourquoi cela a été implémenté? – dnoeth

+2

votre requête existante retournera toujours vrai quand il y a *** n'importe quelle ligne qui se connecte avec succès entre 'table1' et' table2' - vous devez faire cela en fonction des informations clés que vous vérifiez de nouveau (en tant que exemple) – Kritner

+0

Pour autant que j'ai lu, MERGE a ses défauts et comme je le vois, j'ai beaucoup plus de contrôle sur les données, lorsque je fais des mises à jour, INSERTS personnalisés. – Asko

Répondre

0

Au lieu de

if (something) 
update query 
else 
insert query 

Structurez votre logique comme ceci:

update yourTable 
etc 
where some condition is met 

insert into yourTable 
etc 
select etc 
where some condition is met. 
+0

Semble dangereux - potentiellement, une ligne donnée qui répond à la condition sera * d'abord * mise à jour, puis dans une seconde étape * insérée *, aussi?!?!?!? Ne ressemble pas à ce que l'OP recherche ..... –

+0

Les deux conditions ne doivent pas nécessairement être les mêmes. –

+0

J'ai refait ma logique avec quelque chose de similaire (vous pouvez le voir sur le post original). J'ai essayé d'implémenter INSERT où les conditions étaient contrôlées sur quelques tables, rendant l'INSERT très lent (quelques secondes à plusieurs minutes lent). Donc, pour corriger cela, je mets aussi à jour mes lignes de table de données temp avec un statut pour INSERT à éviter. Pas sûr, si c'est la meilleure approche tho. – Asko

0

Vous ne pouvez pas vérifier cela en général, comme vous faites. Vous devez vérifier chaque ID du tableau 2 s'il existe dans le tableau 1 ou non. S'il existe, mettez à jour le Tableau 1 sinon insérez dans le Tableau 1. Cela peut être fait de la manière suivante.

Nous allons itérer sur le tableau 2 pour chaque ID dans SQL en utilisant CURSEURS,

DECLARE @ID int 
DECLARE mycursor CURSOR 
FOR 
SELECT ID FROM Table2 FORWARD_ONLY --Any Unique Column 

OPEN mycursor 
FETCH NEXT FROM mycursor 
INTO @ID 

WHILE @@FETCH_STATUS = 0 
BEGIN 

IF EXISTS (SELECT 1 FROM Table1 WHERE ID = @ID) 
    UPDATE t1 SET t1.data= T2.data --And whatever else you want to update 
    FROM 
    Table1 t1 
    INNER JOIN 
    Table2 t2 
    ON t1.ID = t2.ID --Joining column 
    WHERE t1.id = @ID 
ELSE 
    INSERT INTO Table1 
    SELECT * FROM Table2 WHERE ID = @ID 

FETCH NEXT FROM mycursor 
INTO @ID 
END 
CLOSE mycursor 
DEALLOCATE mycursor 
+0

Est-ce que la déclaration @ID peut également être une valeur de texte? Ou doit-il être int tout le temps? – Asko

+0

Il peut être de n'importe quel type de données. Déclarez simplement une variable et récupérez la valeur de la colonne dans le curseur.Les deux devraient être du même type de données, c'est tout. –