oracle:对于更新选择前10行

时间:2011-06-13 22:24:24

标签: database oracle stored-procedures oracle10g

我有一个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;

请帮我解决一下。

3 个答案:

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