O comportamento que você encontrou para FOR UPDATE SKIP LOCKED foi descrito nesta nota do blog. Meu entendimento é que a cláusula FOR UPDATE é avaliada AFTER a cláusula WHERE. O SKIP LOCKED é como um filtro adicional que garante que entre as linhas que seriam retornadas, nenhuma seja bloqueada.
Sua declaração é logicamente equivalente a:encontre a primeira linha de
card_numbers
e devolva-o se não estiver bloqueado. Obviamente não é isso que você quer. Aqui está um pequeno caso de teste que reproduz o comportamento que você descreve:
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
----------
Nenhuma linha é retornada da segunda seleção. Você pode usar um cursor para contornar esse problema:
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
Como eu busquei explicitamente o cursor, apenas uma linha será retornada (e apenas uma linha será bloqueada).