我正在尝试使用一个程序从cards
表格中随机选择一张卡片c_value
和c_suit
。选择后,该过程应将该条目的taken
字段更新为“Y”。
create or replace procedure j_prc_sel_card(p_value OUT number,
p_suit OUT number)
AS
CURSOR CUR_GET_RAND_CARD IS SELECT c_value,
c_suit
FROM (SELECT c_value,
c_suit,
taken
FROM jackson_card
ORDER BY dbms_random.value)
WHERE rownum = 1
FOR UPDATE OF taken;
BEGIN
OPEN CUR_GET_RAND_CARD;
FETCH CUR_GET_RAND_CARD into p_value, p_suit;
UPDATE jackson_card
SET taken = 'Y'
WHERE c_value = p_value
AND c_suit = p_suit;
CLOSE CUR_GET_RAND_CARD;
END;
然后我试图获取所选卡并输出它作为开始。有了这个:
SET serveroutput on;
DECLARE v_value number;
v_suit number;
BEGIN
j_prc_sel_card(p_value => v_value,p_suit => v_suit);
DBMS_OUTPUT.PUT_LINE(v_value);
DBMS_OUTPUT.PUT_LINE(v_suit);
END;
/
然而,我得到了标题中所述的错误,似乎我选择随机卡的方式阻止我进行更新。提前谢谢!
答案 0 :(得分:2)
以下是对该方案的不同看法(我还在a different answer中解决了您的直接问题)。
鉴于我们确实正在构建一个交易卡计划(而不是针对业务场景使用测试用例),我不喜欢TAKEN
列。更新表列以标记暂时状态似乎是错误的。当我们想要玩另一场比赛时会发生什么?
以下解决方案通过在所有卡中预先填充随机顺序(shuffle)来解决此问题。只需将下一个条目从堆栈中取出即可处理这些卡片。该软件包提供了一种用于耗尽卡片的方法:要么抛出用户定义的异常,要么只是再次在套牌中循环。
create or replace package card_deck is
no_more_cards exception;
pragma exception_init(no_more_cards, -20000);
procedure shuffle;
function deal_one
( p_yn_continuous in varchar2 := 'N')
return cards%rowtype;
end card_deck;
/
create or replace package body card_deck is
type deck_t is table of cards%rowtype;
the_deck deck_t;
card_counter pls_integer;
procedure shuffle is
begin
dbms_random.seed (to_number(to_char(sysdate, 'sssss')));
select *
bulk collect into the_deck
from cards
order by dbms_random.value;
card_counter := 0;
end shuffle;
function deal_one
( p_yn_continuous in varchar2 := 'N')
return cards%rowtype
is
begin
card_counter := card_counter + 1;
if card_counter > the_deck.count()
then
if p_yn_continuous = 'N'
then
raise no_more_cards;
else
card_counter := 1;
end if;
end if;
return the_deck(card_counter);
end deal_one;
end card_deck;
/
这是在行动。如果将连续交易模式设置为LOOP
,则不要使用开放Y
。
SQL> set serveroutput on
SQL>
SQL> declare
2 my_card cards%rowtype;
3 begin
4 card_deck.shuffle;
5 loop
6 my_card := card_deck.deal_one;
7 dbms_output.put_line ('my card is '||my_card.c_suit||my_card.c_value);
8 end loop;
9 exception
10 when card_deck.no_more_cards then
11 dbms_output.put_line('no more cards!');
12 end;
13 /
my card is HA
my card is H7
my card is DJ
my card is CQ
my card is D9
my card is SK
no more cards!
PL/SQL procedure successfully completed.
SQL>
你可能认为我没有处理完整的套牌。你不会是第一个认为那个;)
答案 1 :(得分:1)
您正在使用显式游标,因此您不需要ROWNUM = 1
过滤器。试试这个:
create or replace procedure j_prc_sel_card(p_value OUT number,
p_suit OUT number)
AS
CURSOR CUR_GET_RAND_CARD IS
SELECT c_value,
c_suit,
taken
FROM jackson_card
WHERE taken != 'Y'
ORDER BY dbms_random.value
FOR UPDATE OF taken;
BEGIN
OPEN CUR_GET_RAND_CARD;
FETCH CUR_GET_RAND_CARD into p_value, p_suit;
UPDATE jackson_card
SET taken = 'Y'
WHERE CURRENT OF cur_get_rand_card;
CLOSE CUR_GET_RAND_CARD;
END;
请注意WHERE CURRENT OF
的使用。当我们使用FOR UPDATE CLAUSE
时,这是查找行的最有效方法。如果不使用NOWAIT
子句,如果所选卡被另一个会话锁定,则光标将挂起。当你超越纸牌游戏并进入真实场景时,可能会出现一个不太可能的场景。
另外,请记住,对于真正随机的随机播放,您需要在程序开始时调用DBMS_RANDOM.SEED()
。