2014-01-09 72 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”,并相应地回答。 –