Reputation: 1376
There are 5 coupon code in my Table, these coupon codes are same. If 10 customer's will apply coupon code [FIRST5] simultaneously, then i need to update coupon as "LOCKED" and CUST_ID respectively only for 5 customers. For this case i have tried below SQL to lock the row & get P_KEY to update status & cust id when customer apply coupon. But i could not able to update latest P_KEY for the respective customer. Please advice the correct way of doing.
SELECT P_KEY FROM
(SELECT P_KEY FROM COUPON_DETAILS WHERE COUPON_CODE = 'FIRST5'
AND (STATUS = 'UNLOCK' OR STATUS IS NULL))
WHERE ROWNUM = 1 FOR UPDATE;
P_KEY COUPON_CODE STATUS CUST_ID
1 FIRST5 UNLOCK
2 FIRST5 UNLOCK
3 FIRST5 UNLOCK
4 FIRST5 UNLOCK
5 FIRST5 UNLOCK
Upvotes: 1
Views: 71
Reputation: 17934
If 10 customer's will apply coupon code [FIRST5] simultaneously, then i need to update coupon as "LOCKED" and CUST_ID respectively only for 5 customers.
I don't know of a good, pure-SQL way to do this, because the FOR UPDATE
clause does not affect the query's result set. It only affects how rows are fetched.
So, you might think to try this:
SELECT p_key
FROM coupon_details
WHERE coupon_code = 'FIRST5'
AND (status = 'UNLOCK' OR status IS NULL)
AND rownum = 1
FOR UPDATE SKIP LOCKED;
It's reasonable to think this will cause Oracle to read all the matching coupon_details
rows, skip any that are locked, and then stop after the 1st. But that would only work if the rownum=1
condition was applied after the for update
clause.
Unfortunately, the way it works, the rownum=1
condition is applied first, because the FOR UPDATE
only happens during fetching. So, what winds up happening is that every session looks at the first row only. If it is not locked, it returns that p_key
. But if that first row is locked, it returns no data. (Or, in the case of the query you posted, which did not include SKIP LOCKED
, the sessions after the first one would just wait.)
What you really need to do is select all the rows and then fetch them (skipping locked rows) and then stop after the first one.
You need PL/SQL for that. Here is an example:
DECLARE
c SYS_REFCURSOR;
l_key coupon_details.p_key%TYPE;
BEGIN
-- Open a cursor on all the coupon details that are available to lock
OPEN c FOR
SELECT p_key
FROM coupon_details
WHERE coupon_code = 'FIRST5'
AND (status = 'UNLOCK' OR status IS NULL)
FOR UPDATE SKIP LOCKED;
-- Fetch the first one. The (FOR UPDATE SKIP LOCKED) will ensure that
-- the one we fetch is not locked by another user and, after fetching,
-- will be locked by the current session.
FETCH c INTO l_key;
-- Do what you need with the locked row. In this example, we'll
-- just print some debug messages.
IF l_key IS NULL THEN
DBMS_OUTPUT.PUT_LINE('No free locks!');
ELSE
DBMS_OUTPUT.PUT_LINE('Locked key ' || l_key);
END IF;
-- Close the cursor
CLOSE c;
END;
... be sure to UPDATE coupon_details SET status = 'LOCKED' WHERE p_key = l_key
before you commit.
Upvotes: 1