我有一个
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
语句对于上面的 select 语句失败,并出现错误“ORA-02014:无法从具有 DISTINCT、GROUP BY 等的视图中选择 FOR UPDATE。”
OPEN items_cursor FOR
**select Statement**
FOR UPDATE;
请帮我解决。
这适合您的情况吗?
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)
DCookie 的答案没有解决多会话处理(它只是 FOR UPDATE 语法修复)。如果您不操作行号范围,则每个服务实例都会选择更新相同的行。如果您在两个会话中执行 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 高级队列 (DBMS_AQ) 作为此解决方案的替代方案。
您可以使用
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
的调用,以使其足够慢以便可见)。
根据这篇文章,使用
skip locked
时,选定的行在被提取之前不会被锁定,并且在打开游标后被另一个会话锁定的任何行都将被忽略。
select *
from item
where ROWID in
(
select ROWID from item
order by CREATED_DATE
fetch next 10 rows only
)
for update skip locked