将 BULK COLLECT 与 rownum 一起使用

Posted

技术标签:

【中文标题】将 BULK COLLECT 与 rownum 一起使用【英文标题】:Using BULK COLLECT with rownum 【发布时间】:2018-06-26 06:56:36 【问题描述】:

下面的查询会批量选择,然后循环更新记录。我想在这里使用 BULK COLLECT INTO 和 FORALL 构造来提高性能。但是查询使用 rownum 来更新循环中的列。无论如何我可以在获取 rownum 的同时使用 BULK COLLECT INTO 吗?

     FOR rec IN
        (SELECT rownum rn,
            b.*
        FROM
            (SELECT *
            FROM temp_final a
           WHERE reid  = 1
            AND retype  = 9
            AND sid         = 'r123'
            AND pid = 2191
            ORDER BY a.horder DESC nulls last,
                sname                         ,
                rowgroup                           ,
                dpct DESC nulls last       ,
                name
            ) b
        )
        LOOP
            UPDATE temp_final
            SET horder=rec.rn
             WHERE reid  = 1
            AND retype  = 9
            AND sid         = 'r123'
            AND pid = 2191
            AND mid   =rec.mid;
        END LOOP;

谢谢

【问题讨论】:

您绝对不应该使用 rownum 进行更新 :) 使用您的主键或其他唯一键 感谢 Sudipta 的回复。在 SQL 选择查询中,我将结果按某种顺序排列,我需要在其中一个列中更新该顺序。我认为我不能从主要实现相同的事情或其他键。 :) 如果你想要一个订单,使用排名功能。我建议您重新考虑使用rownum。你应该在 ASK TOM 或其他 *** 问题上阅读它 是的,您可以使用rownum 或您想要的任何其他内容。什么不工作? 您已就康纳的回答向我发表了评论。只是为了回答您的问题,是的,即使我们订购表记录/行,ROWID 也会相同。 rowid 唯一标识每一行的地址,并且不是在运行时生成的,如ROWNUM。 google rowid 让 Oracle 了解更多。 【参考方案1】:

您不需要批量收集,您的 pl/sql 更新尝试执行的操作可以重写为单个 MERGE INTO 语句,这比使用 FORALL 更有效。如果你仍然坚持使用forall,你可以把这个MERGE转换成forall块。

MERGE INTO temp_final tgt USING (
    SELECT rowid,
        ROW_NUMBER() OVER(
            ORDER BY
                horder DESC NULLS LAST,sname,rowgroup,dpct DESC NULLS LAST,name
        ) rn
    FROM
        temp_final
    WHERE
        reid = 1
        AND retype = 9
        AND sid = 'r123'
        AND pid = 2191
)
src ON ( tgt.rowid = src.rowid )
WHEN MATCHED THEN UPDATE SET tgt.horder = src.rn;

【讨论】:

谢谢Kaushik,我已经测试了你的查询。速度更快,结果与原始查询一致。【参考方案2】:

您可以将 rowid 与初始选择一起获取,将批次转储到数组中,然后在您的 FORALL 中使用它,例如

declare
  l_num_list sys.odcinumberlist := sys.odcinumberlist();
  l_char_list sys.odcivarchar2list := sys.odcivarchar2list();
begin  
SELECT rownum rn,rowidtochar(rid)
bulk collect into l_num_list, l_char_list
FROM
            (SELECT a.*, rowid rid
            FROM temp_final a
           WHERE reid  = 1
            AND retype  = 9
            AND sid         = 'r123'
            AND pid = 2191
            ORDER BY a.horder DESC nulls last,
                sname                         ,
                rowgroup                           ,
                dpct DESC nulls last       ,
                name
            ) b;

forall i in 1 .. l_num_list.count
            UPDATE temp_final
            SET horder=l_num_list(i)
            WHERE rowid = chartorowid(l_char_list(i));

end;
/

【讨论】:

@Kaushik Nayak,感谢您的回复。只是为了了解您的“开启”标准,即使我们订购表记录/行,rowid 也会相同?我只想检查您的查询是否会更新预期的相同行。谢谢。 感谢康纳的回复。我会检查一下。 再次感谢康纳。我接受你的回答,尽管我将使用 Merge Into,因为这里的行数会非常少,大约 250 〜所以我认为所有执行时间几乎相同。跨度>

以上是关于将 BULK COLLECT 与 rownum 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

FORALL和BULK COLLECT

BULK COLLECT INTO inside OPEN cursor FOR SELECT... 不填充集合

BULK COLLECT INTO 并且只是 INTO 在一个 sql 块内

oracle plsql中同时记录类型、Collection和Bulk collect

ORACLEBulk Processing with BULK COLLECT and FORALL

BULK COLLECT 的奇怪行为