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);

控制連接順序訪問。

讓我知道。