2010-02-25 10 views
2

J'ai une table simple Sybase, disons qu'il se présente comme suit:Sybase: essayer de verrouiller un enregistrement sur une sélection si un autre appelant ne reçoit pas la même

CREATE TABLE T 
(
    name VARCHAR(10), 
    entry_date datetime, 
    in_use CHAR(1) 
) 

Je veux obtenir l'entrée suivante en fonction de "entry_date" et mettez immédiatement "in_use" à "Y" pour indiquer que l'enregistrement n'est pas disponible pour la requête suivante. Le kicker est que si deux chemins d'exécution tentent d'exécuter la requête en même temps Je veux que le second bloque pour ne pas saisir le même enregistrement. Le problème est que j'ai trouvé que vous ne pouvez pas faire "SELECT FOR UPDATE" dans Sybase si vous avez une clause ORDER BY, donc le proc stocké suivant ne peut pas être créé à cause de l'erreur suivante due à la clause ORDER BY dans select - « « FOR UPDATE » défini de façon incorrecte lorsque vous utilisez un curseur READ ONLY »

y at-il une meilleure façon d'obtenir l'enregistrement suivant, le verrouiller et le mettre à jour en une seule étape atomique


.?
CREATE PROCEDURE dbo.sp_getnextrecord 
@out1 varchar(10) out, 
@out2 datetime out 
AS 
DECLARE @outOne varchar(10), @outTwo datetime 

BEGIN TRANSACTION 

-- Here is the problem area Sybase does not like the 
-- combination of 'ORDER BY' and 'FOR UPDATE' 
DECLARE myCursor CURSOR FOR 
SELECT TOP 1 name, entry_date FROM T 
WHERE in_use = 'N' 
ORDER BY entry_Date asc FOR UPDATE OF in_use 

OPEN myCursor 

FETCH NEXT FROM myCursor 
INTO @outOne, @outOne 

-- Check @@FETCH_STATUS to see if there are any more rows to fetch. 
WHILE @@FETCH_STATUS = 0 
BEGIN 

    UPDATE t SET IN_USE = 'Y' WHERE 
    name = @outOne AND entry_date = @outTwo 

    SELECT @out1 = @outOne 
    SELECT @out2 = @outTwo 

    -- This is executed as long as the previous fetch succeeds. 
    FETCH NEXT FROM myCursor 
     INTO @outOne, @outTwo 
END 

CLOSE myCursor 
DEALLOCATE myCursor 

COMMIT TRANSACTION 

Répondre

0

puisque vous ne sélectionnez qu'une rangée (TOP 1), pourquoi ne pas utiliser un standard locking hint et d'oublier le curseur:

BEGIN TRANSACTION 

SELECT @PK=ID FROM YourTable WITH (UPDLOCK, HOLDLOCK, READPAST) WHERE ... 

UPDATE .... 
WHERE [email protected] 

COMMIT 

si vous avez vraiment besoin de boucle, google "CURSEUR GRATUIT LOOP"

What are the different ways to replace a cursor?

vous pouvez faire une boucle en sélectionnant la prochaine MIN (PK)> @ CurrentPk tout en utilisant les indications de verrouillage sur le SELECT.

+0

Essayé: "SELECT TOP 1 @ outOne = nom, @ outTwo = date_entrée, in_use FROM T AVEC (UPDLOCK, HOLDLOCK, READPAST) OERE in_use = 'N'" Vous avez une erreur en essayant de créer la procédure: 'A L'instruction SELECT qui attribue une valeur à une variable ne doit pas être combinée avec les opérations de récupération de données. –

+0

vous devrez peut-être la sélectionner dans une table #temp_table ou @table_variable (si sybase en a?) En utilisant les indicateurs de verrouillage, puis sélectionnez variable avec un select régulier. –

+0

Les astuces de verrouillage ont fait l'affaire, merci –