web-dev-qa-db-fra.com

Dans SQL Server, dans les conditions de layman, comment verrouillez-vous les ressources suffisantes pour effectuer une transaction insert-si--non-existe?

Si quelqu'un demande comment effectuer un INSERT-IF-NOT-EXISTS Fonctionnement dans SQL Server, ils obtiendront généralement une réponse comme celle-ci:

IF NOT EXISTS(SELECT 1 FROM [TheTable] WHERE [ColumnX] = @valX)
    INSERT [TheTable] ([ColumnX]) VALUES (@valX)

Le problème que je vois avec cela est que, entre la déclaration SELECT et la déclaration INSERT, la situation pourrait changer en externe. Un autre processus pourrait insérer la valeur ColumnXaprès l'instruction SELECT, mais avant la déclaration INSERT, entraînant un Erreur étant soulevée.

J'ai travaillé dans des logiciels pendant un moment, mais je ne suis pas un spécialiste de la DB, et lorsque je recherche une réponse à ce problème dans SQL Server, les résultats que je vois sont non plus pertinents ou assez difficiles à appliquer (parce qu'ils " Référez-vous soit une question différente, soit écrit dans des termes adaptés aux spécialistes de la DB).

Donc, dans les termes de layman, comment résolvez-vous ce problème? J'ai eu un peu rouillé avec SQL dans l'histoire récente, mais je pense qu'il devrait vraiment y avoir un mécanisme de verrouillage pragmatique à utiliser pour cela (que ce soit Il y a ou n'est pas). En cas de retombe, la manutention d'erreur peut peut-être déterminer spécifiquement si une erreur soulevée correspond à ce problème exact, en l'ignorant dans ce cas spécifique.

De préférence, cela n'implique pas simplement verrouiller toute la table à chaque fois.

4
Panzercrisis

Option 1: Prenez un verrouillage qui verrouille au moins la plage de l'indice que la ligne existerait.

SET XACT_ABORT ON;
BEGIN TRAN
IF NOT EXISTS(SELECT 1 FROM [TheTable] WITH (UPDLOCK, HOLDLOCK) WHERE [ColumnX] = @valX)
    INSERT [TheTable] ([ColumnX]) VALUES (@valX)
COMMIT

HOLDLOCK donnera une sémantique et une serrure sérialisables la plage entre les touches existantes dans l'index dans lequel la valeur s'adapterait (s'il y a un index approprié, sinon elle verra la table entière). UPDLOCK Réduit la probabilité d'impasse dans ce modèle car deux requêtes simultanées ne peuvent pas retirer la même plage de verrouillage de la phase de lecture.

Option 2: Vous pouvez simplement ajouter une contrainte unique sur ColumnX et essayer l'insert de toute façon et attraper l'erreur soulevée de la violation clé en double.

Étant donné que l'option 1 a besoin d'un index avec une colonne de pointe ColumnX De toute façon pour répondre à votre préférence de ne pas "verrouiller toute la table à chaque fois", vous pourriez aussi bien en ajouter un et le définir comme unique. L'index accélérera la vérification de l'existence quand même. Avec cela en place, je choisirais entre les options 1 et 2 sur la base de la fréquence, j'attends souvent des tentatives d'insérer des doublons.

4
Martin Smith