Skip to content
Advertisement

lock and get next n records from table in oracle

I have a oracle table PHONE_NUMBER which has 2 columns PHONE and STATUS

U stands for used , O is open/UnUSED .

in my input I will get n as input number , which signifies the number of phones I want to reserve(update status to U from O )

I want a query that will lock(something like SELECT FOR UPDATE) the any n available row (rows that are not locked yet and having status as O )

Constraints: Table has millions of Used and unsed phones

Updates for comments:

problem is i Want n (input) rows to be locked with a particular status . i.e i have to work with rownum

I will be getting multiple concurrent calls in my service with sqli_n as input number of records to select : so

this query wont work for me it will select only first n rows and if the first n rows are locked in 1 call the next/concurrent call will be stuck /wait until the first call updates the status to Used .

for update no wait will fail the above query

for update skip lock will also not work with row num

Advertisement

Answer

To resolve this situation you need to use a for update cursor, because a for update clause is evaluated after the where:

| PLAN_TABLE_OUTPUT                                                                                    |
| :--------------------------------------------------------------------------------------------------- |
| EXPLAINED SQL STATEMENT:                                                                             |
| ------------------------                                                                             |
| select /*+gather_plan_statistics*/ * from t where   decode(status, 'O',                              |
| id) is not null   and rownum < 10 order by decode(status, 'O', id) asc                               |
| for update skip locked                                                                               |
|                                                                                                      |
| Plan hash value: 2984481354                                                                          |
|                                                                                                      |
| ---------------------------------------------------------------------------------------------------- |
| | Id  | Operation                     | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | |
| ---------------------------------------------------------------------------------------------------- |
| |   0 | SELECT STATEMENT              |          |      1 |        |      9 |00:00:00.01 |      21 | |
| |   1 |  FOR UPDATE                   |          |      1 |        |      9 |00:00:00.01 |      21 | |
| |*  2 |   COUNT STOPKEY               |          |      1 |        |      9 |00:00:00.01 |      10 | |
| |   3 |    TABLE ACCESS BY INDEX ROWID| T        |      1 |     49 |      9 |00:00:00.01 |      10 | |
| |*  4 |     INDEX FULL SCAN           | IX_SFU_T |      1 |     10 |      9 |00:00:00.01 |       1 | |
| ---------------------------------------------------------------------------------------------------- |
|                                                                                                      |
| Predicate Information (identified by operation id):                                                  |
| ---------------------------------------------------                                                  |
|                                                                                                      |
|    2 - filter(ROWNUM<10)                                                                             |
|    4 - filter("T"."SYS_NC00005$" IS NOT NULL)                                                        |
|                                                                                                      |

But for explicit cursor with for update skip locked you lock only fetched rows and no rownum evaluation exists in the query: you limit the number of locked rows with limit clause of fetch.

Additionally, to improve the query performance you can use index on expression that evaluates to null on the cases out of interest (where status != ‘O’ in your case). In the upper query I’ve used decodewith the id column as output to assign rows in some predefined order (to use ordering on the same expression that was indexed to avoid table sorting), but if you do not need any order, index expression can be decode(status, 'O', status).

Full setup is below. db<>fiddle here

ID | VAL        |           Q | STATUS
-: | :--------- | ----------: | :-----
20 | 8XS78B92M2 |  1263442844 | O     
40 | XLLUN9DLV4 |  -435709224 | O     
60 | 2DZ4EUH11J | -1328077826 | O     
80 | PJKQMOBISR |  -644473876 | O     
| PLAN_TABLE_OUTPUT                                                                                    |
| :--------------------------------------------------------------------------------------------------- |
| EXPLAINED SQL STATEMENT:                                                                             |
| ------------------------                                                                             |
| select /*+gather_plan_statistics*/ * from t where   decode(status, 'O',                              |
| id) is not null   and rownum < 5 order by decode(status, 'O', id) asc                                |
| for update skip locked                                                                               |
|                                                                                                      |
| Plan hash value: 2984481354                                                                          |
|                                                                                                      |
| ---------------------------------------------------------------------------------------------------- |
| | Id  | Operation                     | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | |
| ---------------------------------------------------------------------------------------------------- |
| |   0 | SELECT STATEMENT              |          |      1 |        |      4 |00:00:00.01 |       8 | |
| |   1 |  FOR UPDATE                   |          |      1 |        |      4 |00:00:00.01 |       8 | |
| |*  2 |   COUNT STOPKEY               |          |      1 |        |      4 |00:00:00.01 |       2 | |
| |   3 |    TABLE ACCESS BY INDEX ROWID| T        |      1 |      4 |      4 |00:00:00.01 |       2 | |
| |*  4 |     INDEX FULL SCAN           | IX_SFU_T |      1 |        |      4 |00:00:00.01 |       1 | |
| ---------------------------------------------------------------------------------------------------- |
|                                                                                                      |
| Predicate Information (identified by operation id):                                                  |
| ---------------------------------------------------                                                  |
|                                                                                                      |
|    2 - filter(ROWNUM<5)                                                                              |
|    4 - filter("T"."SYS_NC00005$" IS NOT NULL)                                                        |
|                                                                                                      |
S_ID | ID —: | –: 0 | 320 0 | 340 0 | 360 0 | 380 0 | 400 1 | 220 1 | 240 1 | 260 1 | 280 1 | 300 2 | 120 2 | 140 2 | 160 2 | 180 2 | 200 3 | 20 3 | 40 3 | 60 3 | 80 3 | 100
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement