从Oracle中的SELECT与Cursor一起插入

时间:2016-03-14 15:30:19

标签: sql oracle plsql

我们有一个运行时间太长的流程,因此我调查了一些优化,发现它编写得非常糟糕,或者至少我认为。

通常这就是它所做的......首先,获取必要的字段来识别每条记录:

Select key1, key2, key2 from a remote_view@remote_host_dblink;

然后程序循环遍历所有结果,一次查询每个记录的相同远程视图,然后将每个记录插入本地主机。这对我来说似乎很愚蠢,所以我开始重新编写进程以获取第一个查询中的所有数据,而不仅仅是键,如下所示:

insert into localtable(col1, col2, col3 ... col15)
select col1, col2, col3 ... col15 FROM remote_view@remotehost;

这个过程进展不顺利 - 它挂了30分钟,所以DBA杀了它。我不知道这个视图是如何写在远程主机上的,我所知道的是它似乎对该系统非常不利。

所以,问题是:当在查询dblink时,从select中插入是否有一些固有的低效率?如果我先将所有列查询到游标中,然后在我们结束时一次执行一个插入,这会更好吗?

1 个答案:

答案 0 :(得分:1)

由于服务器不同,dbLinks之间的大规模操作可能存在风险。 "速度",网络开销,......

一种方法可能是你刚才描述的两种方式之间的中途,即大量操作记录块,而不是所有记录。 您可以尝试使用以下内容(伪代码):

DECLARE
    /* define a cursor on your view */
    CURSOR curXXX  IS
        Select key1, key2, key2 from a remote_view@remote_host_dblink;
    /* define structures to keep your data */
    TYPE tyTabKey1 IS TABLE OF key1%type
        INDEX BY     
    TYPE tyTabKey2 IS TABLE OF key2%type
        INDEX BY PLS_INTEGER;;    
    TYPE tyTabKey3 IS TABLE OF key3%type
        INDEX BY PLS_INTEGER;            
    vTabKey1 tyTabKey1;
    vTabKey2 tyTabKey2;
    vTabKey3 tyTabKey3;
    ...
    /* define how many rows you want to process at a time */
    kLimit number := 5000;
BEGIN        
    OPEN curXXX;    
    LOOP
        /* fetch the wanted number of rows */
        FETCH curXXX
            BULK COLLECT INTO SOG_SOGGETTO_ID,
                 SOG_SOGGETTO_COD,
                 SOG_DATA_INI,
                 SOG_VERSIONE
            LIMIT kLimit;
        /* massively insert the rows */
        FORALL i IN 1 .. vTabKey1.COUNT
            insert into localtable(col1, col2, col3 ... col15)
                     VALUES ( vTabKey1(i),
                              vTabKey2(i),
                              ...
                            );

        num    := num + vTabKey1.COUNT;
        COMMIT;
        /* loop while there are still rows to insert */
        EXIT WHEN SOG_SOGGETTO_ID.COUNT < kLimit;
    END LOOP;

    CLOSE curXXX;
END;
/

在这个例子中我放了一个COMMIT;如果您决定以同样的方式执行此操作,请注意处理错误,因为如果出现错误,您可以进行部分插入。