Cer*_*vEd 5 postgresql join sql-server update
我最近开始将个人项目从 Microsoft SQL Server 转换为 PostgreSQL,我对UPDATE JOIN
在两个表之间执行时遇到的糟糕性能感到惊讶。
假设它们看起来像:
CREATE TABLE foo (
id INTEGER NOT NULL PRIMARY KEY,
bar INTEGER NULL
);
CREATE TABLE foo2 (
id INTEGER NOT NULL PRIMARY KEY,
bar INTEGER NULL
);
Run Code Online (Sandbox Code Playgroud)
在 T-SQL 中,我会使用这样的连接来进行更新:
UPDATE foo
SET bar = t2.bar
FROM foo t1
JOIN foo2 t2
ON t1.id = t2.id;
Run Code Online (Sandbox Code Playgroud)
但是在 Postgres 中运行,查询速度非常慢。
如果我将其更改为:
UPDATE foo
SET bar = t2.bar
FROM foo2 t2
WHERE foo.id = t2.id;
Run Code Online (Sandbox Code Playgroud)
这不是问题。
我知道语法是不同的,但我希望查询优化器能在同一个球场上解决一些问题。相反,事情变得疯狂。除了语法差异之外,我看不到的两个查询之间是否存在细微差别?
Update on foo (cost=85852.43..6211995294.24 rows=338326628280 width=1027)
-> Nested Loop (cost=85852.43..6211995294.24 rows=338326628280 width=1027)
-> Seq Scan on foo (cost=0.00..145721.10 rows=582410 width=1010)
-> Materialize (cost=85852.43..247935.91 rows=580908 width=17)
-> Hash Join (cost=85852.43..241627.37 rows=580908 width=17)
Hash Cond: (t1.id = t2.id)
-> Seq Scan on foo t1 (cost=0.00..145721.10 rows=582410 width=10)
-> Hash (cost=75754.08..75754.08 rows=580908 width=15)
-> Seq Scan on foo2 t2 (cost=0.00..75754.08 rows=580908 width=15)
Run Code Online (Sandbox Code Playgroud)
Update on foo (cost=87575.47..535974.25 rows=581621 width=1022)
-> Hash Join (cost=87575.47..535974.25 rows=581621 width=1022)
Hash Cond: (foo.id = t2.id)
-> Seq Scan on foo (cost=0.00..151301.17 rows=1140417 width=1011)
-> Hash (cost=75761.21..75761.21 rows=581621 width=36)
-> Seq Scan on foo2 t2 (cost=0.00..75761.21 rows=581621 width=36)
Run Code Online (Sandbox Code Playgroud)
Erw*_*ter 10
但是在 Postgres 中运行,查询速度非常慢。
UPDATE foo
SET bar = t2.bar
FROM foo t1
JOIN foo2 t2 ON t1.id = t2.id;
Run Code Online (Sandbox Code Playgroud)
foo
和之间没有连接条件t1
,隐式CROSS JOIN
强制笛卡尔积,即O(N²)
(!)更新操作而不是仅仅O(N)
。结果是不确定的废话。查询计划中的效果也很明显:rows=338326628280
而不是rows=581621
(另外:两个计划都是从稍微不同的表中生成的,但这似乎与问题无关。)
可以通过添加连接条件来修复,例如:
UPDATE foo
SET bar = t2.bar
FROM foo t1
JOIN foo2 t2 ON t1.id = t2.id
WHERE foo.id = t1.id; -- !
Run Code Online (Sandbox Code Playgroud)
好吧,从技术上讲,这是一个WHERE
条件,但都是一样的。
但这只是给猪涂口红。虽然id
是每个表的 PK 列,但这只是增加了噪音。使用您已经找到的命令:
UPDATE foo
SET bar = t2.bar
FROM foo2 t2
WHERE foo.id = t2.id;
Run Code Online (Sandbox Code Playgroud)
不要将目标表作为 a 重复,
from_item
除非您打算进行自联接(在这种情况下,它必须以别名出现在 中from_item
)。
和:
当存在
FROM
子句时,本质上发生的是目标表与from_item
列表中提到的表连接,连接的每个输出行代表目标表的更新操作。使用时,FROM
您应该确保连接最多为要修改的每一行生成一个输出行。换句话说,目标行不应连接到其他表中的多行。如果是这样,则只有一个连接行将用于更新目标行,但将使用哪一个是不容易预测的。
如果您需要一个LEFT [OUTER] JOIN
额外的表,这样的自连接是有意义的(甚至是必要的!)。可悲的是,SQL 中没有规定要"FROM LEFT"
在UPDATE
. 例子:
归档时间: |
|
查看次数: |
438 次 |
最近记录: |