OR条件使查询执行缓慢

时间:2012-05-10 08:22:07

标签: sql oracle

我正在尝试使用WHERE子句执行查询(在Oracle SQL中),如下所示:

    SELECT booking_line_id
FROM booking_line
WHERE    (booking_line_id in
  (SELECT sbli.booking_line_id
   FROM search_booking_line_index sbli,
     policy_record pr,
     policy_record_revision prr
   WHERE sbli.attribute_name = 2209
   AND sbli.policy_record_id = pr.policy_record_id
   AND pr.latest_revision_id = prr.policy_record_revision_id
   AND LOWER(prr.broker_reference) LIKE '%123%'));

执行计划:

|   0 | SELECT STATEMENT       |                        | 19027 |   334K|   455   (3)| 00:00:03 |
|   1 |  NESTED LOOPS          |                        | 19027 |   334K|   455   (3)| 00:00:03 |
|   2 |   VIEW                 | VW_NSO_1               | 19027 |   241K|   449   (2)| 00:00:03 |
|   3 |    HASH UNIQUE         |                        | 19027 |   798K|            |          |
|*  4 |     HASH JOIN          |                        | 19027 |   798K|   449   (2)| 00:00:03 |
|*  5 |      HASH JOIN         |                        |   464 | 13456 |    31   (4)| 00:00:01 |
|*  6 |       TABLE ACCESS FULL| POLICY_RECORD_REVISION |   464 |  9280 |    21   (0)| 00:00:01 |
|*  7 |       TABLE ACCESS FULL| POLICY_RECORD          |  4067 | 36603 |     9   (0)| 00:00:01 |
|*  8 |      INDEX RANGE SCAN  | SBLI_ATTRIBUTE_CDX     |   166K|  2282K|   416   (1)| 00:00:03 |
|*  9 |   INDEX UNIQUE SCAN    | BOOKING_LINE_PK        |     1 |     5 |     1   (0)| 00:00:01 |

此查询执行速度非常快,并且可以立即返回行。但是如果我添加OR条件如下,则查询会挂起(或者需要很长时间):

    SELECT booking_line_id
FROM booking_line
WHERE 1=0 or   (booking_line_id in
  (SELECT sbli.booking_line_id
   FROM search_booking_line_index sbli,
     policy_record pr,
     policy_record_revision prr
   WHERE sbli.attribute_name = 2209
   AND sbli.policy_record_id = pr.policy_record_id
   AND pr.latest_revision_id = prr.policy_record_revision_id
   AND LOWER(prr.broker_reference) LIKE '%123%'));

执行计划:

|   0 | SELECT STATEMENT               |                           |   166K|   811K|   484   (2)| 00:00:03 |
|*  1 |  FILTER                        |                           |       |       |            |          |
|   2 |   INDEX FAST FULL SCAN         | BOOKING_LINE_PK           |   166K|   811K|    66   (5)| 00:00:01 |
|   3 |   NESTED LOOPS                 |                           |     1 |    43 |   419   (1)| 00:00:03 |
|   4 |    NESTED LOOPS                |                           |     1 |    23 |   418   (1)| 00:00:03 |
|*  5 |     INDEX RANGE SCAN           | SBLI_ATTRIBUTE_CDX        |     1 |    14 |   417   (1)| 00:00:03 |
|*  6 |     TABLE ACCESS BY INDEX ROWID| POLICY_RECORD             |     1 |     9 |     1   (0)| 00:00:01 |
|*  7 |      INDEX UNIQUE SCAN         | POLICY_RECORD_PK          |     1 |       |     1   (0)| 00:00:01 |
|*  8 |    TABLE ACCESS BY INDEX ROWID | POLICY_RECORD_REVISION    |     1 |    20 |     1   (0)| 00:00:01 |
|*  9 |     INDEX UNIQUE SCAN          | POLICY_RECORD_REVISION_PK |     1 |       |     1   (0)| 00:00:01 |

预计这应该与第一个查询相同。但令人惊讶的是,它没有。

任何人都可以帮我解释同样的原因吗?

2 个答案:

答案 0 :(得分:2)

至少在11gR2中,计划和执行完全相同。这是我的设置:

CREATE TABLE table1 AS SELECT * FROM dba_objects WHERE object_id IS NOT NULL;
CREATE TABLE table2 AS SELECT * FROM dba_objects WHERE object_id IS NOT NULL 
                                                   AND object_type = 'VIEW';

ALTER TABLE table1 ADD CONSTRAINT table1_pk PRIMARY KEY (object_id);
ALTER TABLE table2 ADD CONSTRAINT table2_pk PRIMARY KEY (object_id);

第一个查询的执行计划:

SQL> EXPLAIN PLAN FOR SELECT * FROM TABLE1
  2                    WHERE OBJECT_ID IN (SELECT OBJECT_ID FROM TABLE2);

SQL> SELECT * FROM TABLE(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2776518249
--------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |           | 31523 |  6772K|   584   (1)|
|   1 |  MERGE JOIN                  |           | 31523 |  6772K|   584   (1)|
|   2 |   TABLE ACCESS BY INDEX ROWID| TABLE1    |   396K|    78M|   575   (1)|
|   3 |    INDEX FULL SCAN           | TABLE1_PK |   396K|       |    77   (0)|
|*  4 |   SORT JOIN                  |           | 31523 |   400K|    10  (30)|
|   5 |    INDEX FULL SCAN           | TABLE2_PK | 31523 |   400K|     7   (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("OBJECT_ID"="OBJECT_ID")
       filter("OBJECT_ID"="OBJECT_ID")

SQL>

和第二个查询:

SQL> EXPLAIN PLAN FOR SELECT * FROM TABLE1
  2             WHERE 1 = 0 OR OBJECT_ID IN (SELECT OBJECT_ID FROM TABLE2); 

SQL> SELECT * FROM TABLE(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2776518249
--------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |           | 31523 |  6772K|   584   (1)|
|   1 |  MERGE JOIN                  |           | 31523 |  6772K|   584   (1)|
|   2 |   TABLE ACCESS BY INDEX ROWID| TABLE1    |   396K|    78M|   575   (1)|
|   3 |    INDEX FULL SCAN           | TABLE1_PK |   396K|       |    77   (0)|
|*  4 |   SORT JOIN                  |           | 31523 |   400K|    10  (30)|
|   5 |    INDEX FULL SCAN           | TABLE2_PK | 31523 |   400K|     7   (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("OBJECT_ID"="OBJECT_ID")
       filter("OBJECT_ID"="OBJECT_ID")

答案 1 :(得分:0)

似乎因为你让服务器改变了执行计划。 第一个查询是使用内连接执行的,这就是为什么它如此之快。 但是在第二个过程中,必须检查第一个条件,然后根据需要进行选择。您可以像这样更改查询:

select ... 
 from t
 left join t2
 where 1=0 or t2.id is not null