INSERT INTO SELECT 与 INSERT from Cursor in PL/SQL

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

所以我正在工作中处理这个项目,我注意到很多人使用 INSERT INTO SELECT 方法:

INSERT INTO candy_tbl (candy_name, 
                       candy_type, 
                       candy_qty) 
SELECT food_name, 
       food_type, 
       food_qty 
       FROM food_tbl WHERE food_type = 'C';

但是,我使用以下光标方法:

FOR rec IN ( SELECT 
             food_name, 
             food_type, 
             food_qty 
             FROM food_tbl WHERE food_type = 'C') 
LOOP INSERT INTO candy_tbl(candy_name, 
                           candy_type, 
                           candy_qty) 
                    VALUES(rec.food_name,
                           rec.food_type, 
                           rec.food_qty) 
END LOOP;

这将进入 PL/SQL 包。我的问题是,哪种方法通常是“首选”方法以及何时?我通常选择游标方法,因为它在异常处理方面给了我更多的灵活性。但是,我可以看到插入大量记录时可能会出现性能问题。

oracle-database plsql plsqldeveloper
4个回答
3
投票

FOR 循环需要从 CURSOR 中获取每一行。循环中的INSERT将一一发生。PLSQL在PLSQL引擎中运行,SQL在SQL引擎中运行,因此FOR循环: - 在PLSQL引擎中运行 - 将查询发送到 SQL 引擎以执行查询并打开游标,然后切换回 PLSQL 引擎 - 每个循环从 CURSOR 执行 FETCH,然后执行 INSERT,这意味着返回到 SQL 引擎,然后返回到 PLSQL 引擎

SQL 和 PLSQL 之间的每次切换以及每次 FETCH 都很昂贵。

INSERT INTO SELECT 将被发送到 SQL 引擎一次并在那里运行直到完成,然后返回到 PLSQL。

还存在其他优点,但这是这两种方法之间的主要 PLSQL 区别。


0
投票

第一个更快,因为它基本上是单个事务,也称为基于集合的处理。

后者按行操作,对于非常大的表,性能会有很大差异。


0
投票

如果您确实需要游标处理的灵活性但需要更好的性能,则可以使用第三个中间选项 - BULK COLLECT 和 FORALL 以及保存异常选项。 然而,代价是代码复杂性更高。以下是基本结构。

declare
    exception error_in_forall ; 
    pragma exception_init (error_in_forall, -24381);

    cursor c_select is ( select ... ) ;
    type   c_array_type table of c_select%rowtype;     
    v_select_data  c_array_type ; 

begin 
    open c_select; 
    loop
        fetch c_select
         bulk collect 
         into v_select_data; 
        forall rdata in v_select_data.first .. v_select_data.last save exceptions
            insert into ( ... ) values (v_select_data(rdata).column ... ) ;    
exceptions 
    when error_in_forall then
        <Process Oracle generated bulk error collection > 
end ;

完成后,如果在执行插入期间发生任何错误,则会触发一次异常。 Oracle 已构建一个 SQL%BULK_EXCEPTIONS 集合,其中包含索引值和每个错误代码。 有关详细信息,请参阅适用于您的版本的 PL/SQL 语言参考。


0
投票

U 可以尝试在 for rec 之前为第一个查询添加 Begin,并在结束循环之后添加 END,如下所示

开始 对于录音输入(选择 食物名称, 食物类型, 食物数量 来自 food_tbl WHERE food_type = 'C') 循环插入 candy_tbl(candy_name, 糖果类型, 糖果数量) 值(rec.food_name, 记录.食物类型, 记录食物数量) 结束循环; 结束;

© www.soinside.com 2019 - 2024. All rights reserved.