2011-05-25 2 views
7

Il existe beaucoup d'informations sur Internet concernant ce "problème" courant.Sql Server 2005 - Insérer s'il n'existe pas

Solutions comme:

IF NOT EXISTS() BEGIN INSERT INTO (...) END 

ne sont pas thread-safe à mon avis et vous seront probablement d'accord.

Cependant, pourriez-vous confirmer que mettre l'existence dans la clause where d'un seul select résoudrait le problème de la concurrence la plus élevée dans le moteur sql? Est-ce suffisant?

insert into Table (columns) 
select column1, column2, column3 
where not exists (select top 1 1 from Table where something) 

Faut-il y a également ajouté un certain niveau de transaction ou peut-il être exécuté sur un par défaut: commis?

Est-ce que cela fonctionnerait sous le niveau non-validé?

Merci!

// ajouté plus tard

Puis-je supposer que les deux sql sont corrects:

1) jeu niveau d'isolation de transaction répétitive lire

IF NOT EXISTS() BEGIN INSERT INTO (...) END 

2) définir le niveau d'isolation transaction de lecture répétée

insert into Table (columns) 
select column1, column2, column3 
where not exists (select top 1 1 from Table where something) 
+1

Non, il ne fonctionnera pas à l'une ou ReadCommitted niveau non engagé. Vous auriez besoin de quelques conseils de verrouillage supplémentaires. duplication possible de [Insertion d'une ligne seulement si elle n'est pas déjà là] (http: // stackoverflow.com/questions/3407857/only-insertion-a-row-if-its-not-already-there) –

+1

Vous n'avez pas besoin de "help out" existe clauses - ils sont assez intelligents pour terminer après avoir vu 1 rang. vous pouvez juste faire EXISTS (SELECT * FROM ... 'et il fait la bonne chose –

+0

il fera la bonne chose, mais dans le cas de la coccyx il n'est pas suffisamment sûr pour le filetage et les erreurs de violation primaires peuvent se produire sous haute Donc, selon le lien de @Martin, une transaction répétable d'isolation de lecture devrait être ajoutée – Paul

Répondre

6

Avec TRY/CAT CH vous pouvez éviter le supplément lire

BEGIN TRY 
    INSERT etc 
END TRY 
BEGIN CATCH 
    IF ERROR_NUMBER() <> 2627 
     RAISERROR etc 
END CATCH 
  • NOT EXISTS lira la table, que ce soit dans la FI ou WHERE
  • L'INSERT nécessite une lecture pour vérifier l'unicité

Si vous le pouvez jeter les doublons, c'est une technique hautement évolutive

Liens:

+0

Eh bien, mais si je devais insérer ou mettre à jour alors: Pourrais-je faire: si error_number() = 2627 commencer la mise à jour ... fin? – Paul

+0

@Paul: voir mon dernier lien puis pour traiter les UPDATEs – gbn

+0

Est-il possible d'élever l'erreur par défaut dans begin catch block? ou Dois-je explixity obtenir le numéro d'erreur et le message pour satisfaire les paramètres de raiserror? – Paul

1

Pour répondre à la question mise à jour repeatable read ne serait toujours pas suffisant. Il s'agit du niveau holdlock/serializable que vous auriez besoin.

Vous essayez d'éviter phantoms (où sur la première lecture aucune ligne ne répondaient aux critères pour que les rendements NOT EXISTS vrai, mais par la suite d'une transaction concurrente insère une ligne réunion, il)

+0

Il semble que je vais commencer à être paranoïaque lors de l'écriture des instructions SQL :) – Paul

+0

@Paul - 'serializable' vous donnerait également le risque de blocage, Cela pourrait être évité avec' UPDLOCK, HOLDLOCK'. Je voudrais utiliser 'TRY ... CATCH' moi-même cependant. –