oracle中的性能调优

时间:2014-03-18 07:14:52

标签: sql oracle plsql database-performance

我编写了以下pl / sql代码,它通过连接两个表table_1和table_2来根据某些条件更新user_five列。此更新适用于table_1中连接col1的所有记录。但这花费了太多时间。如何调整这个。非常感谢任何帮助。

DECLARE
ISIN_CHECK         VARCHAR2(25) := '&3';
cursor c1 is SELECT col1,col2,col3,col4,col5 FROM table_1
WHERE col1 IN (SELECT col1 FROM table_2 WHERE col_n='&2');
rec_count1 number := 1;
 rec_count2 number := 1;
 rec_count3 number := 1;

 BEGIN
--dbms_output.put_line('start');
--dbms_output.put_line('Parm - 3' || ISIN_CHECK);
if (ISIN_CHECK ='ISIN_EGHT') then
--dbms_output.put_line(' entered into isin user_eighy');
for i in c1
loop
    UPDATE table_1 b
    SET  b.USER_FIVE=i.col2
    WHERE  b.col1 in(SELECT col1 FROM table_2 WHERE col_n='&2')
    AND b.col1=i.col1
    AND b.col5=i.col5
    AND b.cs_flag='C';
  rec_count1 := rec_count1 + 1;
  if mod(rec_count1,10) = 0 then
  commit;
--dbms_output.put_line(b.user_five);
  end if;
  end loop;
 ELSIF (ISIN_CHECK ='ISIN_ELVN') then
for i in c1
loop
   UPDATE table_1 b
    SET  b.USER_FIVE=i.col3
    WHERE  b.col1 in(SELECT col1 FROM table_2 WHERE col_n='&2')
    AND b.col1=i.col1
    AND b.col5=i.col5
    AND b.cs_flag='C';
   rec_count2 := rec_count2 + 1;
if mod(rec_count2,10) = 0 then
  commit;
--dbms_output.put_line(b.user_five);
  end if;
  end loop;
ELSE
for i in c1
 loop
      UPDATE table_1 b
    SET  b.USER_FIVE=i.col4
    WHERE  b.col1 in(SELECT col1 FROM table_2 WHERE col_n='&2')
    AND b.col1=i.col1
    AND b.col5=i.col5
    AND b.cs_flag='C';
  rec_count3 := rec_count3 + 1;
if mod(rec_count3,10000) = 0 then
  commit;
--dbms_output.put_line(b.user_five);
   end if;
   end loop;
  END IF;
   COMMIT;

 END;
 /

3 个答案:

答案 0 :(得分:1)

我很难弄清楚你究竟想做什么。我认为第一个循环归结为以下更新语句:

update table_1 b
   set b.user_five = b.col2
 where b.col1 in (select col1
                    from table_2
                   where col_n = '&2')
   and b.cs_flag = 'C';

但是你使用这样一个精心设计的结构,很难说。也许你可以解释一下你想要更新的内容。

因此,尽可能尝试使用简单的更新语句。 如果你确实需要一个plsql for循环,请不要在循环中放置commit。这完全没必要。

答案 1 :(得分:0)

我要做的第一件事就是用大的IF THEN ELSE替换三个只有一个的FOR循环,而不用任何IF语句:

for i in c1
loop
    UPDATE table_1 b
    SET  b.USER_FIVE= CASE ISIN_CHECK
                        WHEN 'ISIN_EGHT' THEN i.col2
                        WHEN 'ISIN_ELVN' THEN i.col3
                        ELSE i.col4 
                      END
    WHERE  b.col1 in(SELECT col1 FROM table_2 WHERE col_n='&2')
    AND b.col1=i.col1
    AND b.col5=i.col5
    AND b.cs_flag='C';

但是这个代码可以更简化....没有任何两个表的连接,游标选择table_1中的行,更新也更新table_1,我只使用一个更新:

UPDATE table_1 b
SET  b.USER_FIVE= CASE ISIN_CHECK
                    WHEN 'ISIN_EGHT' THEN b.col2
                    WHEN 'ISIN_ELVN' THEN b.col3
                    ELSE b.col4 
                  END
WHERE  b.col1 in(SELECT col1 FROM table_2 WHERE col_n='&2')
AND b.cs_flag='C';

我想这可以加快程序50~100次。

我在这里假设,table_1中的行是由col1标识的唯一行,否则后一个更新不等同于您的过程,因为在这种情况下此过程不是确定性的。看一下简单的情况,比如table_1中有以下行,ISIN_CHECK =' ISIN_EGHT' (所以我们从col3中取值将它们放入USER_FIVE中):

+---------------------+
| col1 col3 col4 col5 |
+---------------------+
| 1     1    1    1   |
| 1     2    2    1   |
| 1     3    3    1   |
+---------------------+

游标按某种顺序撤销行 - 但由于游标的SQL查询中没有ORDER BY子句, Oracle不保证任何订单。它可以随机返回1 2 3,或3 2 1或2 3 1 - 。该过程针对每次迭代更新table_1(更新表中所有匹配行的USER_FIVE),并且上述情况的最终结果可以根据游标返回的行的顺序而不同 - >所有匹配行的USER_FIVE将更新为光标从最后一行检索的值,在这种情况下,它可以是1,2或3,但没人知道哪一行。

答案 2 :(得分:0)

加快查询速度,您可以进行此更改

cursor c1 is SELECT col1,col2,col3,col4,col5 FROM table_1替换为 cursor c1 is SELECT rowid r, col1,col2,col3,col4,col5 FROM table_1

更改sql游标中的条件 这必须改变 'UPDATE table_1 b SET b.USER_FIVE=i.col2 WHERE b.col1 in(SELECT col1 FROM table_2 WHERE col_n='&2') AND b.col1=i.col1 AND b.col5=i.col5 AND b.cs_flag='C';

用这个

UPDATE table_1 b SET b.USER_FIVE=i.col2 WHERE b.col1 in(SELECT col1 FROM table_2 WHERE col_n='&2') AND b.rowid = i.r AND b.cs_flag='C';

并且必须为所有更新语句执行此操作