oracle:要更新,请选择前 10 行

问题描述 投票:0回答:4

我有一个

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;

请帮我解决。

database oracle stored-procedures oracle10g
4个回答
2
投票

这适合您的情况吗?

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
投票

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) 作为此解决方案的替代方案。


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
的调用,以使其足够慢以便可见)。

根据这篇文章,使用

skip locked
时,选定的行在被提取之前不会被锁定,并且在打开游标后被另一个会话锁定的任何行都将被忽略。


0
投票
select *
from item 
where ROWID in
(
  select ROWID from item 
  order by CREATED_DATE
  fetch next 10 rows only
)
for update skip locked
© www.soinside.com 2019 - 2024. All rights reserved.