4

我有一个sql查询,当连接两个表时速度很慢,但当我第一次查询一个表并且查询另一个表时,速度很快。加入Oracle时查询速度慢,分裂成两个查询时速度快

环境/先决条件

Oracle版本:
Oracle数据库11g企业版发布11.2.0.3.0 - 64位生产

对象涉及:
cfc_materialized(物化视图,21.5神达行)
contact(table,12,6 Mio rows)

涉及的指标:
CREATE INDEX CONTACT_CLIENT ON CONTACT(客户);
CREATE INDEX CFC_MATERIALIZED_A_S_T ON CFC_MATERIALIZED(ASSOCIATION_TYPE,SOURCEID,TARGETID);

我已经重新计算2代表与级联的统计=>真:

BEGIN 
    SYS.DBMS_STATS.GATHER_TABLE_STATS (
    OwnName  => '...' 
    ,TabName  => '...' 
    ,Estimate_Percent => 0 
    ,Degree   => 4 
    ,Cascade   => TRUE 
    ,No_Invalidate  => FALSE); 
END; 
/

问题

我有以下查询:

SELECT c.* 
FROM contact c 
WHERE c.client in (
    SELECT cfc.targetid 
    FROM cfc_materialized cfc 
    WHERE cfc.sourceid = 'e95027f0-a83e-11e3-a0ae-005056aebabc' 
     AND cfc.association_type = 'ContactDataSharing' 
) 
    AND c.deleted = 0; 

解释计划:

-------------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation      | Name    | Rows | Bytes | Cost (%CPU)| Time  | TQ |IN-OUT| PQ Distrib | 
-------------------------------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |     | 11M| 2214M| 38976 (1)| 00:07:48 |  |  |   | 
| 1 | PX COORDINATOR    |     |  |  |   |   |  |  |   | 
| 2 | PX SEND QC (RANDOM)   | :TQ10001   |  |  |   |   | Q1,01 | P->S | QC (RAND) | 
| 3 | NESTED LOOPS    |     |  |  |   |   | Q1,01 | PCWP |   | 
| 4 |  NESTED LOOPS    |     | 11M| 2214M| 38976 (1)| 00:07:48 | Q1,01 | PCWP |   | 
| 5 |  SORT UNIQUE    |     | 2164 | 196K| 10672 (1)| 00:02:09 | Q1,01 | PCWP |   | 
| 6 |  PX RECEIVE    |     | 2164 | 196K| 10672 (1)| 00:02:09 | Q1,01 | PCWP |   | 
| 7 |  PX SEND HASH   | :TQ10000   | 2164 | 196K| 10672 (1)| 00:02:09 | Q1,00 | P->P | HASH  | 
| 8 |   PX BLOCK ITERATOR  |     | 2164 | 196K| 10672 (1)| 00:02:09 | Q1,00 | PCWC |   | 
|* 9 |   MAT_VIEW ACCESS FULL | CFC_MATERIALIZED | 2164 | 196K| 10672 (1)| 00:02:09 | Q1,00 | PCWP |   | 
|* 10 |  INDEX RANGE SCAN   | CONTACT_CLIENT | 5500 |  | 37 (0)| 00:00:01 | Q1,01 | PCWP |   | 
|* 11 |  TABLE ACCESS BY INDEX ROWID| CONTACT   | 5474 | 550K| 973 (0)| 00:00:12 | Q1,01 | PCWP |   | 
-------------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    9 - filter("CFC"."SOURCEID"='e95027f0-a83e-11e3-a0ae-005056aebabc' AND "CFC"."ASSOCIATION_TYPE"='ContactDataSharing') 
    10 - access("C"."CLIENT"="CFC"."TARGETID") 
    11 - filter("C"."DELETED"=0)enter code here 

这需要很长时间,我想知道为什么在cfc_materialized上有完整的访问权限。 将查询拆分为2个查询时,速度要快得多。 第一次查询:

SELECT cfc.targetid 
FROM cfc_materialized cfc 
WHERE cfc.sourceid = 'e95027f0-a83e-11e3-a0ae-005056aebabc' 
    AND cfc.association_type = 'ContactDataSharing'; 

该查询返回2个targetids。 解释计划:

------------------------------------------------------------------------------------------- 
| Id | Operation  | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |      | 2164 | 196K| 36 (0)| 00:00:01 | 
|* 1 | INDEX RANGE SCAN| CFC_MATERIALIZED_A_S_T | 2164 | 196K| 36 (0)| 00:00:01 | 
------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - access("CFC"."ASSOCIATION_TYPE"='ContactDataSharing' AND 
       "CFC"."SOURCEID"='e95027f0-a83e-11e3-a0ae-005056aebabc') 

这里是第二个查询。我用的第一个查询的输出作为输入的IN参数:

SELECT * 
FROM contact c 
WHERE c.client in (
    'e95027f0-a83e-11e3-a0ae-005056aebabc', 
    'eb37d3b0-a83e-11e3-a0ae-005056aebabc' 
) 
    AND c.deleted = 0; 

解释计划:

----------------------------------------------------------------------------------------------- 
| Id | Operation     | Name   | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |    | 2814 | 283K| 505 (0)| 00:00:07 | 
| 1 | INLIST ITERATOR    |    |  |  |   |   | 
|* 2 | TABLE ACCESS BY INDEX ROWID| CONTACT  | 2814 | 283K| 505 (0)| 00:00:07 | 
|* 3 | INDEX RANGE SCAN   | CONTACT_CLIENT | 2827 |  | 23 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("C"."DELETED"=0) 
    3 - access("C"."CLIENT"='e95027f0-a83e-11e3-a0ae-005056aebabc' OR 
       "C"."CLIENT"='eb37d3b0-a83e-11e3-a0ae-005056aebabc') 

问题

所以我的问题是,为什么是CBO不以类似的方式执行查询,例如手动执行2个查询中的查询。我也试图与提示查询在2个查询使用相同的指标,如:

SELECT /*+ index(c CONTACT_CLIENT) */ c.* 
FROM contact c 
WHERE c.client in (
    SELECT /*+ index(cfc CFC_MATERIALIZED_A_S_T) */ cfc.targetid 
    FROM cfc_materialized cfc 
    WHERE cfc.sourceid = 'e95027f0-a83e-11e3-a0ae-005056aebabc' 
     AND cfc.association_type = 'ContactDataSharing' 
) 
    AND c.deleted = 0; 

但后来我得到一个更糟糕的解释计划:

------------------------------------------------------------------------------------------------------- 
| Id | Operation     | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |      | 11M| 2214M| 305K (1)| 01:01:09 | 
| 1 | NESTED LOOPS    |      |  |  |   |   | 
| 2 | NESTED LOOPS    |      | 11M| 2214M| 305K (1)| 01:01:09 | 
| 3 | SORT UNIQUE    |      | 2164 | 196K| 36 (0)| 00:00:01 | 
|* 4 |  INDEX RANGE SCAN   | CFC_MATERIALIZED_A_S_T | 2164 | 196K| 36 (0)| 00:00:01 | 
|* 5 | INDEX RANGE SCAN   | CONTACT_CLIENT   | 5500 |  | 37 (0)| 00:00:01 | 
|* 6 | TABLE ACCESS BY INDEX ROWID| CONTACT    | 5474 | 550K| 973 (0)| 00:00:12 | 
------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    4 - access("CFC"."ASSOCIATION_TYPE"='ContactDataSharing' AND 
       "CFC"."SOURCEID"='e95027f0-a83e-11e3-a0ae-005056aebabc') 
    5 - access("C"."CLIENT"="CFC"."TARGETID") 
    6 - filter("C"."DELETED"=0) 

我也试图做一个连接,而不是IN子句的像亚历山大和StanislavL建议:

SELECT c.* 
FROM contact c 
JOIN cfc_materialized cfc ON c.client = cfc.targetid 
WHERE cfc.sourceid = 'e95027f0-a83e-11e3-a0ae-005056aebabc' 
    AND cfc.association_type = 'ContactDataSharing' 
    AND c.deleted = 0; 

,并得到了以下解释计划,这又是很慢(比两个单独的查询速度较慢):

--------------------------------------------------------------------------------------------- 
| Id | Operation   | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
--------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |      | 11M| 2214M| 51745 (1)| 00:10:21 | 
|* 1 | HASH JOIN   |      | 11M| 2214M| 51745 (1)| 00:10:21 | 
|* 2 | INDEX RANGE SCAN | CFC_MATERIALIZED_A_S_T | 2164 | 196K| 36 (0)| 00:00:01 | 
|* 3 | TABLE ACCESS FULL| CONTACT    | 12M| 1237M| 51649 (1)| 00:10:20 | 
--------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - access("C"."CLIENT"="CFC"."TARGETID") 
    2 - access("CFC"."ASSOCIATION_TYPE"='ContactDataSharing' AND 
       "CFC"."SOURCEID"='e95027f0-a83e-11e3-a0ae-005056aebabc') 
    3 - filter("C"."DELETED"=0) 
+0

另外,我相信,在这里需要'cfc_materialized.targetid'上的索引 – Alexander

回答

0
SELECT c.* 
FROM contact c 
    join (
    SELECT cfc.targetid as client 
    FROM cfc_materialized cfc 
    WHERE cfc.sourceid = 'e95027f0-a83e-11e3-a0ae-005056aebabc' 
     AND cfc.association_type = 'ContactDataSharing' 
) sub ON c.client=sub.client 
    AND c.deleted = 0; 

使用加入,而不是IN避免WHERE

+0

为什么你认为查询将被计算多次? – Alexander

0

每一行的同一个查询的计算主要是,避免在Contact表的全表的访问。 避免c.*,指定列的具体列表。 重建索引

CREATE INDEX CFC_MATERIALIZED_A_S_T ON CFC_MATERIALIZED(TARGETID, ASSOCIATION_TYPE, SOURCEID);

控制连接顺序访问。

让我知道。