【问题标题】:oracle: For update select first 10 rowsoracle:对于更新选择前 10 行
【发布时间】:2011-09-14 07:02:30
【问题描述】:

我有一个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: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.”

OPEN items_cursor FOR
**select Statement**
FOR UPDATE;

请帮我解决一下。

【问题讨论】:

    标签: database oracle stored-procedures oracle10g


    【解决方案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)
    

    【讨论】:

    • 是的。有用。非常感谢。我在 created_date 和 item_id 上有索引。我还能做些什么来提高性能?
    【解决方案2】:

    您可以使用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;
    /
    

    如果您从两个会话中同时运行它,它们将获得不同的对象(尽管您可能需要在 found 块内启用对 dbms_lock.sleep 的调用以使其足够慢以显示)。

    根据this post,当使用skip locked 时,选定的行在被获取之前不会被锁定,并且在游标打开后被另一个会话锁定的任何行都将被忽略。

    【讨论】:

    • 在同一篇文章中,Rob 还解释说它无助于提高性能。
    • @lk_zeif - 我只是在寻找在这个阶段可能有用的东西,并没有真正考虑性能;但它确实允许多个会话至少同时运行,而不会相互绊倒。当然,它们都打在同一张桌子上会产生一些影响,但它们不会互相阻挡 - 它们不会排队并串联运行。
    • @lk_zeif - 性能信息在 this post 中,您在另一个问题中链接到该信息;我不记得以前读过那个。我没有注意到两个会话的延迟,但是在我的小例子中,它可能会在睡眠的噪音中丢失。如果尝试这种方法,绝对需要考虑和检查,所以感谢您指出。
    • @ik_zelf - 呃,很抱歉在两个 cmets 中破坏了您的用户名,并且没有及时注意到编辑其中任何一个 *8-)
    • 别担心,开心点,我可以忍受。我们从错误中学习。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-04
      • 1970-01-01
      • 2014-02-10
      • 1970-01-01
      • 1970-01-01
      • 2011-04-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多