如果您将到目前为止的答案放在一起,进行清理和改进,您将得到这个高级查询:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
这比他们中的任何一个都快得多。将当前接受的答案的性能提高 10 - 15 倍(在我对 PostgreSQL 8.4 和 9.1 的测试中)。
但这仍然远非最佳。使用NOT EXISTS
(反)半连接以获得更好的性能。EXISTS
是标准 SQL,一直存在(至少从 PostgreSQL 7.2 开始,早在问这个问题之前)并且完全符合提出的要求:
UPDATE sales s
SET status = 'ACTIVE'
WHERE NOT EXISTS (
SELECT FROM sales s1 -- SELECT list can be empty for EXISTS
WHERE s.saleprice = s1.saleprice
AND s.saledate = s1.saledate
AND s.id <> s1.id -- except for row itself
)
AND s.status IS DISTINCT FROM 'ACTIVE'; -- avoid empty updates. see below
db<>fiddle here
旧 SQL Fiddle
标识行的唯一键
如果您没有表的主键或唯一键(id
在示例中),您可以用系统列替换ctid
此查询的目的(但不能用于其他一些目的):
AND s1.ctid <> s.ctid
每个表都应该有一个主键。如果您还没有,请添加一个。我建议 Postgres 10+ 中的一个serial
或一个IDENTITY
专栏。
有关的:
这怎么更快?
EXISTS
一旦发现第一个欺骗,反半连接中的子查询就可以停止评估(没有必要进一步研究)。对于几乎没有重复的基表,这只是稍微更有效。有了很多重复,这变得更有效率。
排除空更新
对于已经具有status = 'ACTIVE'
此更新的行,不会更改任何内容,但仍会以全部成本插入新的行版本(适用少数例外情况)。通常,您不希望这样。添加另一个WHERE
条件,如上所示,以避免这种情况并使其更快:
如果status
已定义NOT NULL
,则可以简化为:
AND status <> 'ACTIVE';
列的数据类型必须支持<>
运算符。有些类型json
不喜欢。看:
NULL 处理的细微差别
此查询(与Joel 当前接受的答案不同)不会将 NULL 值视为相等。以下两行将(saleprice, saledate)
被视为“不同”(虽然看起来与人眼相同):
(123, NULL)
(123, NULL)
还传入一个唯一索引和几乎其他任何地方,因为根据 SQL 标准,NULL 值不比较相等。看:
OTOH, GROUP BY
,DISTINCT
或DISTINCT ON ()
将 NULL 值视为相等。根据您想要实现的目标使用适当的查询样式。您仍然可以使用这个更快的查询来IS NOT DISTINCT FROM
代替=
任何或所有比较,以使 NULL 比较相等。更多的:
如果定义了所有被比较的列NOT NULL
,则没有分歧的余地。