如果你把到目前为止的答案放在一起,清理和改进,你会得到这个高级的问题:
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 < >小提琴
老sqlfiddle
标识行的唯一键
如果你没有表的主键或唯一键(例子中的id),你可以用系统列ctid来代替这个查询(但不是为了其他目的):
AND s1.ctid <> s.ctid
每个表都应该有一个主键。如果你还没有,就加一个。我建议在Postgres 10+中设置一个系列或IDENTITY专栏。
相关:
有序序列生成
自动递增表列
这怎么更快?
EXISTS反半连接中的子查询可以在发现第一个欺骗时立即停止求值(没有必要进一步查找)。对于副本很少的基表来说,这只是稍微提高了一些效率。有了大量的副本,这就变得更有效率了。
排除空更新
对于已经有status = 'ACTIVE'的行,这个更新不会改变任何东西,但仍然会以全成本插入一个新的行版本(轻微例外适用)。通常情况下,你不希望这样。添加另一个如上所示的WHERE条件来避免这种情况,并使其更快:
如果status定义为NOT NULL,可以简化为:
AND status <> 'ACTIVE';
列的数据类型必须支持<>操作符。有些类型,如json,则不会。看到的:
如何查询一个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,则没有不一致的余地。