[英]Optimization of multiple inner self-joins by unique key
假設我有一些帶有事務的表X
,其中CUSTOMER_ID
是主鍵。
我也有數百個“功能”(就機器學習而言),即對該表X
的查詢文本。 所有這些查詢就像:
查詢1:
SELECT
X.CUSTOMER_ID,
WHEN(X.GENDER = "F" AND X.IS_PREGNANT = TRUE) THEN 1 OTHERWISE 0 AS WILL_BUY_FOR_KIDS
FROM X
查詢xxx:
SELECT
X.CUSTOMER_ID,
WHEN(X.GENDER = "M" AND X.AVG_AMOUNT > 1000) THEN 1 OTHERWISE 0 AS RICH_DUDE
FROM X
任務是生成具有從X
表計算出的所有“功能”的表。 因此,我需要使用“功能”查詢的文本(以編程方式)創建輸出查詢的文本。 就像是:
SELECT
*
FROM SOME_QUERY_1
INNER JOIN SOME_QUERY_X
ON SOME_QUERY_1.CUSTOMER_ID = SOME_QUERY_X.CUSTOMER_ID
...
當內部自我連接數百個子查詢時,上述輸出查詢可能會非常慢。 顯然,如果SQL引擎將該查詢“重寫”為類似(避免連接)的內容,那將很酷:
SELECT
CUSTOMER_ID,
WHEN(X.GENDER = "F" AND X.IS_PREGNANT = TRUE) THEN 1 OTHERWISE 0 AS WILL_BUY_FOR_KIDS,
WHEN(X.GENDER = "M" AND X.AVG_AMOUNT > 1000) THEN 1 OTHERWISE 0 AS RICH_DUDE,
.....
FROM X
幾個問題:
(a + b) * a = a^2 + b*a
)。 關系代數有這樣的規則嗎? 看來Oracle的優化程序可以完成這項工作。
drop table x;
create table x (a int primary key,b int);
select x0.b,x1.b,x2.b,x3.b,x4.b,x5.b,x6.b,x7.b,x8.b,x9.b
from (select x.a,x.b from x) x0
join (select x.a,x.b from x) x1 on x1.a = x0.a
join (select x.a,x.b from x) x2 on x2.a = x0.a
join (select x.a,x.b from x) x3 on x3.a = x0.a
join (select x.a,x.b from x) x4 on x4.a = x0.a
join (select x.a,x.b from x) x5 on x5.a = x0.a
join (select x.a,x.b from x) x6 on x6.a = x0.a
join (select x.a,x.b from x) x7 on x7.a = x0.a
join (select x.a,x.b from x) x8 on x8.a = x0.a
join (select x.a,x.b from x) x9 on x9.a = x0.a
;
注意執行計划中的9 ELIMINATE_JOIN 。
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 26 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| X | 1 | 26 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
1 - SEL$44564B95 / X@SEL$2
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
FULL(@"SEL$44564B95" "X"@"SEL$2")
OUTLINE(@"SEL$3")
OUTLINE(@"SEL$2")
OUTLINE(@"SEL$1")
MERGE(@"SEL$3")
MERGE(@"SEL$2")
OUTLINE(@"SEL$5428C7F1")
OUTLINE(@"SEL$5")
OUTLINE(@"SEL$4")
MERGE(@"SEL$5428C7F1")
MERGE(@"SEL$5")
OUTLINE(@"SEL$730B2DEF")
OUTLINE(@"SEL$7")
OUTLINE(@"SEL$6")
MERGE(@"SEL$730B2DEF")
MERGE(@"SEL$7")
OUTLINE(@"SEL$DE510E9C")
OUTLINE(@"SEL$9")
OUTLINE(@"SEL$8")
MERGE(@"SEL$DE510E9C")
MERGE(@"SEL$9")
OUTLINE(@"SEL$6C54F645")
OUTLINE(@"SEL$11")
OUTLINE(@"SEL$10")
MERGE(@"SEL$6C54F645")
MERGE(@"SEL$11")
OUTLINE(@"SEL$5E3B1022")
OUTLINE(@"SEL$13")
OUTLINE(@"SEL$12")
MERGE(@"SEL$5E3B1022")
MERGE(@"SEL$13")
OUTLINE(@"SEL$D60B40D8")
OUTLINE(@"SEL$15")
OUTLINE(@"SEL$14")
MERGE(@"SEL$D60B40D8")
MERGE(@"SEL$15")
OUTLINE(@"SEL$B8655000")
OUTLINE(@"SEL$17")
OUTLINE(@"SEL$16")
MERGE(@"SEL$B8655000")
MERGE(@"SEL$17")
OUTLINE(@"SEL$EC740ABE")
OUTLINE(@"SEL$19")
OUTLINE(@"SEL$18")
MERGE(@"SEL$EC740ABE")
MERGE(@"SEL$19")
OUTLINE(@"SEL$7AC5A3AA")
OUTLINE(@"SEL$20")
MERGE(@"SEL$7AC5A3AA")
OUTLINE(@"SEL$F6D45FB3")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$17")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$15")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$13")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$11")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$9")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$7")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$5")
ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$3")
OUTLINE(@"SEL$5A225B26")
ELIMINATE_JOIN(@"SEL$5A225B26" "X"@"SEL$19")
OUTLINE_LEAF(@"SEL$44564B95")
ALL_ROWS
DB_VERSION('11.2.0.2')
OPTIMIZER_FEATURES_ENABLE('11.2.0.2')
IGNORE_OPTIM_EMBEDDED_HINTS
END_OUTLINE_DATA
*/
Column Projection Information (identified by operation id):
1 - "X"."B"[NUMBER,22]
Note
-----
- dynamic sampling used for this statement (level=2)
首先,您應將查詢編寫為:
SELECT X1.A * 2, // Some operation on X1.A
X2.B / 2 // Some operation on X2.B
FROM X X1 JOIN
X X2
ON X1.C = X2.C;
子查詢沒有提供任何值(但我稍后會再講)。
如果C
被聲明為unique
(或primary key
),則該字段上有一個索引。 我很確定每個數據庫仍然會執行JOIN
,但這確實非常快:
X1
)並找到C
C
X2
。 與首先讀取數據相比,用於索引的額外開銷不是很大的負擔。
使用您編寫的查詢,大多數數據庫都足夠聰明,仍然可以進行此優化-他們只是忽略了子查詢。 MySQL是一個例外。 它實際上實現了子查詢,因此您編寫的版本可能會令人討厭地緩慢-實現也會失去使用索引的能力。 但是,重寫應該可以。
您可能會問,為什么數據庫對此沒有優化。 好吧,數據庫開發人員更專注於優化寫得好的查詢,而不是優化寫得不好的查詢。 如果不需要JOIN
,那么查詢write應該知道這一點。 (實際上, JOIN
確實有一種用途,它過濾掉NULL
值。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.