2014-01-09 71 views
0

Postgres數據庫,下面的兩個查詢相當於性能?每個人都說「聯接總是比子查詢更快」,但查詢規劃器是否將子查詢優化爲聯合幕後更新與子查詢與加入更新 - 哪一個更好的性能

查詢1:

UPDATE table_a 
SET col_1 = 'a fixed value' 
WHERE col_2 IN (
    SELECT col_2 FROM table_b 
); 

解釋計劃:

 
Update on table_a (cost=0.00..9316.10 rows=1 width=827) 
    -> Nested Loop Semi Join (cost=0.00..9316.10 rows=1 width=827) 
     -> Seq Scan on table_a (cost=0.00..9287.20 rows=1 width=821) 
     -> Index Scan using idx_table_b on table_b (cost=0.00..14.45 rows=1 width=14) 
       Index Cond: (col_2 = (table_a.col_2)::numeric) 

問題2:

UPDATE table_a ta 
SET col_1 = 'a fixed value' 
FROM table_b tb 
WHERE ta.col_2 = tb.col_2; 

解釋計劃:

 
Update on table_a ta (cost=0.00..9301.67 rows=1 width=827) 
    -> Nested Loop (cost=0.00..9301.67 rows=1 width=827) 
     -> Seq Scan on table_a ta (cost=0.00..9287.20 rows=1 width=821) 
     -> Index Scan using idx_table_b on table_b tb (cost=0.00..14.45 rows=1 width=14) 
       Index Cond: (col_2 = (ta.col_2)::numeric) 

我相信他們是在結果當量(請提醒我,如果我錯了)。我已經嘗試了幾個解釋計劃具有大量的數據集。看起來它們在性能上是等價的,無論是在更新整個表格時還是將table_a.col_2限制爲一個小子集時。

我想確定我不會錯過什麼。如果它們相同,你會選擇哪一個?爲什麼?

+0

它們在功能上是否相同?在第一種情況下,只有匹配的行將被更新,其他行將保留其值。在第二種情況下,不匹配的行將變爲NULL NULL – hashbrown

+0

@hashbrown **錯誤。**我剛測試它 - 非匹配的行將不會**變爲NULL。您必須對第三種可能的結構感到困惑:'UPDATE table_a SET col_1 =(SELECT'固定值'FROM table_b where table_a.col_2 = table_b.col_2')'。在這個構造中,是的,不匹配的行將**變爲NULL,並且col_2必須是唯一的。 – ADTC

+0

如果你一直在看解釋輸出,請包括它!此外,請提供有用的表格定義和虛擬數據以匹配您的查詢,所以我們不必製作這些東西。 –

回答

2

不Postgres的查詢規劃優化子查詢到連接幕後?

通常,是的。

不要猜測,看看查詢計劃。

考慮:

 
CREATE TABLE table_a (col_1 text, col_2 integer); 
CREATE TABLE table_b (col_2 integer); 
INSERT INTO table_b(col_2) VALUES (1),(2),(4),(NULL); 
INSERT INTO table_a (col_1, col_2) VALUES ('a fixed value', 2), ('a fixed value', NULL), ('some other value', 2); 
ANALYZE table_a; 
ANALYZE table_b; 

比較:

 
regress=> explain UPDATE table_a 
SET col_1 = 'a fixed value' 
WHERE col_2 IN (
    SELECT col_2 FROM table_b 
); 
           QUERY PLAN         
-------------------------------------------------------------------------- 
Update on table_a (cost=1.09..2.15 rows=2 width=16) 
    -> Hash Semi Join (cost=1.09..2.15 rows=2 width=16) 
     Hash Cond: (table_a.col_2 = table_b.col_2) 
     -> Seq Scan on table_a (cost=0.00..1.03 rows=3 width=10) 
     -> Hash (cost=1.04..1.04 rows=4 width=10) 
       -> Seq Scan on table_b (cost=0.00..1.04 rows=4 width=10) 
(6 rows) 

regress=> explain UPDATE table_a ta 
regress-> SET col_1 = 'a fixed value' 
regress-> FROM table_b tb 
regress-> WHERE ta.col_2 = tb.col_2; 
           QUERY PLAN         
----------------------------------------------------------------------------- 
Update on table_a ta (cost=1.07..2.14 rows=1 width=16) 
    -> Hash Join (cost=1.07..2.14 rows=1 width=16) 
     Hash Cond: (tb.col_2 = ta.col_2) 
     -> Seq Scan on table_b tb (cost=0.00..1.04 rows=4 width=10) 
     -> Hash (cost=1.03..1.03 rows=3 width=10) 
       -> Seq Scan on table_a ta (cost=0.00..1.03 rows=3 width=10) 
(6 rows) 

看到了嗎?同樣的計劃。子查詢已轉換爲聯接。

EXISTS而不是IN來表達它通常比較乾淨。這對於NOT IN vs NOT EXISTS來說更重要,它們在空值的語義上是不同的,所以無論如何它都是一個好習慣。你會寫:

UPDATE table_a a 
SET col_1 = 'a fixed value' 
WHERE EXISTS (SELECT 1 FROM table_b b WHERE b.col_2 = a.col_2); 

這將往往會產生同樣的計劃,但它是一個有點更好IMO - 這不僅是因爲,如果它計劃分解爲連接,相關子查詢通常比巨大的IN列表掃描更可怕。

+0

是的,對於** Q2 **,對於** Q1 **和'更新>嵌套循環>序列掃描>索引掃描',解釋輸出與'更新>嵌套循環半連接>序列掃描>索引掃描'非常相似。我不確定'Semi Join'是什麼,但它是唯一的區別。我將用文本輸出更新問題。 – ADTC

1

IN:

返回true如果指定的值相匹配的子查詢或列表中的任何值。

存在:

返回true,如果子查詢中包含任何行。

加入:

加入上接合第2個結果集。


如果接合柱是UNIQUE然後更快join。如果不是,則IN is faster than JOIN on DISTINCT

看到這篇文章在我的博客爲微軟SQL Server性能的詳細信息,這也可能是有關PostgreSQL的:

+0

感謝您的文章。 PostgreSQL查詢規劃器的行爲可能不同,這就是我想知道的。 – ADTC

+0

這不是最相關的文章,因爲它是一個完全不同的數據庫引擎。當您關注[tag:SQL]標籤時,請記住它的意思是「結構化查詢語言(SQL)」,而不是「Microsoft SQL Server」,並相應地回答。 –