PL SQL查询执行时间过长

时间:2015-11-16 08:21:23

标签: oracle performance plsql

Person表分别在Finance和HR模式中有2百万行和3百万行。现在,我将使用HR模式上的person_status更新Finance模式的Person状态。查询在带有Solaris 10和Oracle 11.2.2的32 GB RAM的M3000服务器上运行。花了117个小时仍在跑步。如何优化此查询。我在person_no上创建了索引。

DECLARE
  CURSOR c IS  
    SELECT p1.person_no     AS "PersonNo1"
         , p2.person_no     AS "PersonNo2"
         , p2.person_status AS "P2_PERSON_STATUS"
      FROM person    p1
         , hr.person p2    
     WHERE (lower(p1.person_no) = lower(p2.person_no)           
           OR substr(p1.person_no, instr(p1.person_no, '-') + 1, length(p1.person_no))
              = substr(p2.person_no, instr(p2.person_no, '-') + 1, length(p2.person_no)));
BEGIN

  FOR i IN c LOOP  
    UPDATE person    
       SET person_status_id = decode(lower(i.P2_PERSON_STATUS), 'y', 1, 'n', 2)    
     WHERE (lower(person_no) = lower(i.PersonNo2)           
        OR substr(person_no, instr(person_no, '-') + 1, length(person_no))
           = substr(i.PersonNo2, instr(i.PersonNo2, '-') + 1, length(i.PersonNo2)));  
    COMMIT;  
  END LOOP;

END;
/

2 个答案:

答案 0 :(得分:2)

您可以采取以下措施来优化此功能。查看查询计划会很有帮助。

您可以使用合并声明。那么你只有一个语句,你可以优化这个语句。像这样的东西

merge into person 
using hr.person person_hr 
on (person.person_no=person_hr.person_no) 
when matched then 
update set person_status_id=decode(lower(person_status),'y',1,'n',2);

您必须调整on部分以匹配您的where声明。

这可能是最佳优化的来源。也许你必须创建一个较低的索引和substr。

这样的东西
CREATE INDEX person_idx
 ON person (lower(person_no))

当然还有子串等希望这会有所帮助。

答案 1 :(得分:0)

在作为问题提供的代码中,我可以看到有两个方面。

1) Query Optimization
2) ROW BY ROW Update --> Never Recommended for such a huge records count..

Approach to Optmization.

1) Use Merge Statement as it will Bundle up the block into pure SQL thus will enhance your query.

2) Use Function based Index as i can see there are lot of functions like "LOWER" is used in the query "WHERE" conditions.
NOTE : [Over INDEXING may also cause deterioration in the performance}

3) Last but not the least if you have to go by Anonymous block only then avoid using ROW BY ROW Update. Try using Bulk collect Options as illustrated below.
Since i do not have workstation handy with me please bear with any Syntax Errors.
Let me know if this helps.

DECLARE
  TYPE p1
IS
  TABLE OF <table_name>.<COLUMN_NAME>%TYPE;
  PersonNo1 p1;
TYPE p2
IS
  TABLE OF <table_name>.<COLUMN_NAME>%TYPE;
  PersonNo2 p2;
TYPE status
IS
  TABLE OF <table_name>.<COLUMN_NAME>%TYPE;
  P2_PERSON_STATUS status;
BEGIN
  SELECT p1.person_no AS "PersonNo1" ,
    p2.person_no      AS "PersonNo2" ,
    p2.person_status  AS "P2_PERSON_STATUS" BULK COLLECT
  INTO PersonNo1,
    PersonNo2,
    P2_PERSON_STATUS
  FROM person p1 ,
    hr.person p2
  WHERE (lower(p1.person_no)                                                  = lower(p2.person_no)
  OR SUBSTR(p1.person_no, instr(p1.person_no, '-') + 1, LENGTH(p1.person_no)) = SUBSTR(p2.person_no, instr(p2.person_no, '-') + 1, LENGTH(p2.person_no)));
  FORALL I                                        IN PersonNo1.FIRST..PersonNo1.LAST
  UPDATE person
  SET person_status_id                                               = DECODE(lower(P2_PERSON_STATUS(I)), 'y', 1, 'n', 2)
  WHERE (lower(person_no)                                            = lower(PersonNo2(I))
  OR SUBSTR(person_no, instr(person_no, '-') + 1, LENGTH(person_no)) = SUBSTR(PersonNo2(I), instr(PersonNo2(I), '-') + 1, LENGTH(PersonNo2(I))));

COMMIT;

END;