Le problème que nous essayons de résoudre se présente comme suit.
Notre solution est d'attribuer un statut à la carte, et le stocker de la date de réservation. En réservant une carte que nous faisons à l'aide de la déclaration "sélectionnez pour la mise à jour ". La requête recherche des cartes disponibles et pour les cartes qui ont été réservés depuis longtemps.
Cependant, notre requête ne fonctionne pas comme prévu.
J'ai préparé une situation simplifiée pour expliquer le problème. Nous avons une table card_numbers, plein de données - toutes les lignes ont des numéros d'identification non nuls. Maintenant, nous allons essayer de verrouiller certains d'entre eux.
-- first, in session 1
set autocommit off;
select id from card_numbers
where id is not null
and rownum <= 1
for update skip locked;
Nous ne commettons pas la transaction ici, la ligne doit être verrouillée.
-- later, in session 2
set autocommit off;
select id from card_numbers
where id is not null
and rownum <= 1
for update skip locked;
Le comportement attendu est que dans les deux séances, nous obtenons une seule autre ligne qui satisfait les conditions de.
Toutefois, il ne fonctionne pas de cette façon. Selon que nous utilisons le " Cheminement verrouillé " partie de la requête ou non - les changements behavious:
Ainsi, après cette longue introduction est la question.
Est-ce le genre de comportement de verrouillage souhaité possible dans Oracle? Si oui, qu'est-ce qu'on fait de mal? Quelle serait la bonne solution?
Le comportement que vous avez rencontré pour la mise à jour Skip verrouillé a été décrit dans ce blog Note . Ma compréhension est que la clause de mise à jour est évaluée après la clause WHERE. Le skip verrouillé est comme un filtre supplémentaire qui garantit que parmi les rangées qui auraient été renvoyées, aucune n'est verrouillée.
Votre déclaration est logiquement équivalente à: trouvez la première ligne de card_numbers
et le retourner s'il n'est pas verrouillé. Évidemment, ce n'est pas ce que vous voulez.
Voici un petit cas de test qui reproduit le comportement que vous décrivez:
SQL> CREATE TABLE t (ID PRIMARY KEY)
2 AS SELECT ROWNUM FROM dual CONNECT BY LEVEL <= 1000;
Table created
SESSION1> select id from t where rownum <= 1 for update skip locked;
ID
----------
1
SESSION2> select id from t where rownum <= 1 for update skip locked;
ID
----------
Aucune ligne n'est renvoyée de la seconde sélection. Vous pouvez utiliser un curseur pour contourner ce problème:
SQL> CREATE FUNCTION get_and_lock RETURN NUMBER IS
2 CURSOR c IS SELECT ID FROM t FOR UPDATE SKIP LOCKED;
3 l_id NUMBER;
4 BEGIN
5 OPEN c;
6 FETCH c INTO l_id;
7 CLOSE c;
8 RETURN l_id;
9 END;
10 /
Function created
SESSION1> variable x number;
SESSION1> exec :x := get_and_lock;
PL/SQL procedure successfully completed
x
---------
1
SESSION2> variable x number;
SESSION2> exec :x := get_and_lock;
PL/SQL procedure successfully completed
x
---------
2
Depuis que j'ai explicitement récupéré le curseur, une seule ligne sera renvoyée (et une seule ligne sera verrouillée).
Tandis que les autres réponses ont déjà expliqué suffisamment ce qui se passe dans votre base de données avec les différents SELECT .. FOR UPDATE
Variantes, je pense qu'il convient de mentionner que Oracle décourage en utilisant FOR UPDATE SKIP LOCKED
directement et encourage à utiliser Oracle AQ
au lieu:
http://download.oracle.com/docs/cd/b28359_01/server.111/b28286/stavements_10002.htm#i2066346
Nous utilisons Oracle AQ
Dans notre application et je peux confirmer que, après une courbe d'apprentissage quelque peu raide, il peut s'agir d'un moyen assez pratique de gérer directement les producteurs/consommateurs dans la base de données.
Ce n'est pas que la réponse de Vincent a mal mais je l'aurais conçu différemment.
Mon premier instinct est de sélectionner pour mettre à jour le premier enregistrement disponible et mettre à jour l'enregistrement avec un "réservé_date". Après que XXX Time a passé et que la transaction n'est pas finalisée, mettez à jour le record de l'enregistrement_date de retour à Null libérant l'enregistrement à nouveau.
J'essaie de garder les choses aussi simples que possible. Pour moi, c'est plus simple.