我有一个ITEM
表,其中一列是CREATED_DATE
。在集群环境中,许多服务副本将从此表中选择项目并对其进行处理。每项服务应从ITEM表中挑选最早的10项。
我可以在存储过程中使用它来选择前10行:
select * from (
select item_id, row_number() over (order by CREATED_DATE) rownumber
FROM item )
where rownumber < 11
由于许多服务应该使用此功能,因此我使用select ... for update
将行更新为“处理”。但是下面的FOR UPDATE
语句对于上面的选择语句失败,错误为“ORA-02014:无法从具有DISTINCT,GROUP BY等的视图中选择FOR UPDATE”。
OPEN items_cursor FOR
**select Statement**
FOR UPDATE;
请帮我解决一下。
答案 0 :(得分:1)
这适用于您的情况吗?
SELECT *
FROM item
WHERE (item_id,created_date) IN
(SELECT item_id,created_date
FROM (SELECT item_id, created_date
, ROW_NUMBER() OVER (ORDER BY created_date) rownumber
FROM item)
WHERE rownumber < 11)
答案 1 :(得分:1)
您可以使用skip locked
和计数器来实现此目的,只要您不一定需要每个会话来获取连续的行。例如:
declare
l_cursor sys_refcursor;
l_name all_objects.object_name%type;
l_found pls_integer := 0;
begin
open l_cursor for
select object_name
from all_objects
order by created
for update skip locked;
loop
fetch l_cursor into l_name;
dbms_output.put_line(l_fetches || ':' || l_name);
if l_cursor%found then
l_found := l_found + 1;
-- dbms_lock.sleep(1);
end if;
exit when l_cursor%notfound or l_found = 10;
end loop;
end;
/
如果从两个会话同时运行它们,它们将获得不同的对象(尽管您可能需要启用dbms_lock.sleep
块内found
的调用以使其足够慢以便可见。
根据this post,当使用skip locked
时,所选行在获取之前不会被锁定,并且在光标打开后由另一个会话锁定的任何行都将被忽略。
答案 2 :(得分:0)
DCookie的答案并不能解决多会话处理问题(它只是FOR UPDATE语法修复)。如果你不操纵rownumber范围,那么每个服务实例如果要选择更新相同的行。如果在两个会话中执行that_for_update_select,则第二个会话将等到第一个完成事务。并行处理将是一种错觉。
我会考虑使用for update skip locked
方法进行有效的批量处理。我的答案如下:
declare
con_limit constant number default 10;
cursor cItems is
select i.item_id, i.created_date
from item i
order by i.created_date
for update skip locked;
type t_cItems is table of cItems%rowtype;
tItems t_cItems;
begin
open cItems;
while true loop
fetch cItems bulk collect into tItems limit con_limit;
-- processing tItems
exit when tItems.count < con_limit;
end loop;
end;
可能的长期交易可能是一个劣势。请考虑使用Oracle Streams Advanced Queuing(DBMS_AQ)作为此解决方案的替代方案。