2013-09-26 21 views
2

通過組合(兩列)主鍵連接兩個表時,在查詢計劃中收到糟糕的基數估計。例如:具有複合主鍵的表的錯誤基數

CREATE TABLE t1 AS SELECT x, x*2 AS x2 FROM generate_series(0, 1000) AS x; 
ALTER TABLE t1 ADD PRIMARY KEY(x, x2); 
ANALYZE t1; 

CREATE TABLE t2 AS SELECT x, x*2 AS x2 FROM generate_series(0, 1000) AS x; 
ALTER TABLE t2 ADD FOREIGN KEY (x, x2) REFERENCES t1(x,x2); 
ANALYZE t2; 

EXPLAIN ANALYZE 
SELECT * 
FROM t1 JOIN t2 USING (x, x2) 

QUERY PLAN                          
------------------------------------------------------------------------------------------------------------- 
Hash Join (cost=30.02..52.55 rows=1 width=8) (actual time=0.660..1.551 rows=1001 loops=1)      
    Hash Cond: ((t1.x = t2.x) AND (t1.x2 = t2.x2))                
    -> Seq Scan on t1 (cost=0.00..15.01 rows=1001 width=8) (actual time=0.021..0.260 rows=1001 loops=1)  
    -> Hash (cost=15.01..15.01 rows=1001 width=8) (actual time=0.620..0.620 rows=1001 loops=1)     
     Buckets: 1024 Batches: 1 Memory Usage: 40kB               
     -> Seq Scan on t2 (cost=0.00..15.01 rows=1001 width=8) (actual time=0.019..0.230 rows=1001 loops=1) 
Total runtime: 1.679 ms  

該計劃預計返回一行,但實際上返回1001行。這在簡單查詢中不是問題,但是在執行復雜查詢時會導致非常慢的查詢計劃。我該如何幫助查詢優化器做得更好?

+0

的問題是,主鍵和外鍵是過分指定的:對於兩個表x2完全依賴於x1的功能。這迷惑了優化者;它無法知道暗含的依賴性。只加入't1.x = t2.x'就可以得出正確的估計值。另外'CREATE TABLE t1 AS SELECT x/100 AS x,x%100 AS x2 FROM generate_series(0,10000)AS x;'(與t2相同)給出了正確的估計值。 – joop

+0

@joop db可以知道連接不會放棄任何行,因爲外鍵用於連接。 –

+0

我意識到這一點。但可能是優化器假定每個關鍵元素都將熵添加到密鑰空間(可能是啓發式選擇的錯誤選擇)。順便說一句:添加一個主鍵到t2沒有幫助。我的div/mod技巧確實改變了預期的行數。 – joop

回答

1

使用其中一列完全依賴於另一列的組合主鍵是一個「有趣」的設計。

在任何情況下,PostgreSQL當前都假定每列的選擇性是相互獨立的,因此將它們相乘(不管它們是否在同一索引中,即使它是主鍵索引),我也不會不知道一個好方法。

您可以使用此迂迴更貼近真實的選擇性:

EXPLAIN ANALYZE 
SELECT * 
FROM t1 JOIN t2 on (t1.x=t2.x and t1.x2 between t2.x2 and t2.x2); 
+0

第二列並非真正意味着依賴於第一列。我只是想不出創建測試數據的更好方法。建議歡迎! –

+0

好戲!它實際上工作。 – joop

0

另一種方法來創建真正的正交關鍵元素:

CREATE TABLE t1 AS SELECT x/100 AS x, x%100 AS x2 FROM generate_series(0, 10000) AS x; 
ALTER TABLE t1 ADD PRIMARY KEY(x, x2); 
ANALYZE t1; 

CREATE TABLE t2 AS SELECT x/100 AS x, x%100 AS x2 FROM generate_series(0, 10000) AS x; 
ALTER TABLE t2 ADD PRIMARY KEY (x, x2) ; -- added PK 
ALTER TABLE t2 ADD FOREIGN KEY (x, x2) REFERENCES t1(x,x2); 

ANALYZE t2; 
+0

謝謝,這確實是創建測試數據的更好方法。 –