2012-08-14 5 views
4

J'ai vu une question ici à propos de cela, mais il était vieux, donc je vais demander à nouveau au cas où une solution existe maintenant.SQL Server 2008: SELECT FOR UPDATE

Mon problème est le suivant. J'ai une table de base de données que je souhaite choisir mais je veux verrouiller les rangées que j'ai choisies. La raison en est que je peux avoir un autre processus en cours d'exécution qui voudra également sélectionner les mêmes lignes et je veux empêcher cela. Imaginez que j'ai deux processus faisant la même chose. On effectue une sélection et commence à effectuer son traitement des données. Ensuite, quelques secondes plus tard, le processus suivant arrive et fait une sélection, mais parce que les lignes ne sont pas verrouillées, il prend également les mêmes enregistrements et commence à les traiter. Il s'agit bien sûr d'une mauvaise situation. Dans Oracle, vous pouvez utiliser SELECT FOR UPDATE qui supprimera un verrou sur les lignes pour les empêcher d'être utilisées par le 2ème processus. Comment cela peut-il être réalisé dans SQL Server 2008?

Je devrais ajouter que je ne peux utiliser que des instructions SQL standard. Je n'ai pas accès aux procédures, fonctions, etc. Cela doit être fait par une simple déclaration. C'est une longue histoire et une considération de conception qui a été enlevée de mes mains. La solution doit pouvoir être stockée dans une table, récupérée plus tard puis exécutée via les objets ADO en C# affectés en particulier à un objet de commande.

Comment un verrou peut-il être appliqué à cette instruction?

SELECT * 
FROM 
    (SELECT TOP (20) * 
    FROM [TMA_NOT_TO_ENTITY_QUEUE] 
    WHERE [TMA_NOT_TO_ENTITY_QUEUE].[STATE_ID] = 2 
    ORDER BY TMA_NOT_TO_ENTITY_QUEUE.ID) a 
+1

Vous pouvez utiliser [l'indicateur de verrouillage] (http://msdn.microsoft.com/en-us/library/ms187373.aspx) - 'select * from tbl (updlock)' –

+2

Après avoir vu votre dernière modification, utilisez nowait pour retourner dès que la ressource est verrouillée: 'select * from tbl (updlock, nowait)' –

+0

Merci, je vais vous donner un essai – CSharpened

Répondre

8

Vous devez utiliser l'un des soi-disant table hints:

le verrou de mise à jour empêche les autres processus de tenter de UPD mangé ou supprimer les lignes en question - mais il n'empêche pas accès en lecture:

SELECT TOP (20) * 
    FROM [TMA_NOT_TO_ENTITY_QUEUE] WITH (UPDLOCK) 
    WHERE [TMA_NOT_TO_ENTITY_QUEUE].[STATE_ID] = 2 
    ORDER BY TMA_NOT_TO_ENTITY_QUEUE.ID 

Il y a aussi un, mais essentiellement verrou exclusif, le verrouillage de la mise à jour devrait être suffisant. Une fois que vous avez sélectionné vos lignes avec un verrou de mise à jour, ces lignes sont "protégées" contre les mises à jour et les écritures jusqu'à la fin de la transaction.

+0

Il semble que vous avez égaré la clause WITH (il devrait être dans la section FROM). – alexey

+0

@alexey: tout à fait raison - réparé - merci! –

2

Vous devez envelopper vos processus dans une transaction, et définir le niveau d'isolation des transactions de façon appropriée (ex: Serializable)

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRAN 
    UPDATE yourtable... 
    -- process 1 
COMMIT TRAN 

et

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRAN 
    UPDATE yourtable... 
    -- process 2 
COMMIT TRAN 

Ce comportement a été dans SQL Server depuis des temps immémoriaux.

D'autres niveaux d'isolation de transaction sont disponibles.

+0

Je devrais ajouter que je ne peux utiliser que des instructions SQL standard. Je n'ai pas accès aux procédures, fonctions, etc. Cela doit être fait par une simple déclaration. C'est une longue histoire et une considération de conception qui a été enlevée de mes mains. Je vais ajouter cette information à ma question initiale. – CSharpened

+0

@CSharpened Vous pouvez initialiser une transaction dans votre code C# (soit via SqlConnection.BeginTransaction ou l'espace de noms System.Transactions en fonction de votre infrastructure) - et définir Data.IsolationLevel dans cette instruction - voir http://msdn.microsoft.com /en-us/library/5ha4240h.aspx par exemple – podiluska

+0

Est-ce la même chose pour les transactions pour Oracle ou d'autres bases de données? Je demande seulement que ma solution soit conçue pour être agnostique de base de données ainsi je ne peux pas coder tout datasource spécifique. Encore une autre décision prise de mes mains :) – CSharpened

3

par verrou, que voulez-vous arriver avec le deuxième processus? Si vous voulez attendre que le premier se termine, vous pouvez le faire en utilisant le niveau d'isolation de la transaction.

essayer d'exécuter ce petit test et vous comprendrez:

Ouvrir une deux nouvelles requêtes sur SSMS (permet de l'appeler A et B à partir de maintenant un) et A, créer une table simple comme ceci:

create table transTest(id int) 
insert into transTest values(1) 

maintenant, procédez comme suit:

do select * from transTest dans les deux d'entre eux.Vous verrez la valeur 1

sur une course:

set transaction isolation level read committed 

Le B run:

begin transaction 
insert into transTest values(2) 

sur une course:

select * from transTest 

vous verrez que la requête wont terminer car il est verrouillé par la transaction sur B

Le B run:

commit transaction 

Revenir à A et vous verrez que la requête a terminé

Répétez le test avec le niveau d'isolation de transaction réglée lecture non validée sur A, vous verrez que la requête ne sera pas verrouillé par la transaction

+0

Salut merci pour la réponse. Je voudrais simplement que le deuxième processus ne soit pas autorisé. Je ne veux pas attendre le 1er processus pour libérer le verrou. Je préférerais qu'il renvoie une exception occupée de ressources par exemple afin que je puisse le gérer moi-même et ensuite attendre une minute ou deux pour relancer le processus. J'ai ajouté mon choix ci-dessus. – CSharpened

0

Mécanisme de verrouillage de SQL Server et Oracle complètement différent (même opposé dans le comportement). Si vous incluez des éléments de contrôle de niveau de transaction dans votre code, ce ne sera pas "agnostique de base de données", mais aucun code de base de données d'une complexité raisonnable jamais "base de données agnostique" de toute façon. Dans le cas où vous devez utiliser uniquement le langage SQL, restez dans la spécification SQL92 et contrôlez les transactions côté application sans utiliser de lien vers SQL, mais cela limitera votre capacité à écrire une solution efficace (spécifique à la base de données).