我需要检索表中的所有行,其中2列组合在一起是不同的。因此,我希望所有在同一天没有任何其他销售的销售以相同的价格出售。基于日期和价格的唯一销售将更新为活动状态。
所以我在想:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
但是我的大脑比那更远了。
答案 0 :(得分:400)
SELECT DISTINCT a,b,c FROM t
大致等同于:
SELECT a,b,c FROM t GROUP BY a,b,c
习惯GROUP BY语法是一个好主意,因为它更强大。
对于您的查询,我会这样做:
UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
SELECT id
FROM sales S
INNER JOIN
(
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(*) = 1
) T
ON S.saleprice=T.saleprice AND s.saledate=T.saledate
)
答案 1 :(得分:315)
如果你把答案汇总到目前为止,清理和改进,你会得到这个优越的问题:
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&lt;&gt;小提琴here
Old SQL Fiddle
如果您没有表的主键或唯一键(示例中为id
),则可以使用系统列ctid
替换此查询(但不是其他一些目的):
AND s1.ctid <> s.ctid
每个表都应该有一个主键。如果还没有,请添加一个。我建议在Postgres 10 +中使用serial
或IDENTITY
列。
相关:
EXISTS
反半连接中的子查询可以在找到第一个欺骗后立即停止评估(没有必要进一步查看)。对于具有少量重复的基表,这只是稍微更有效。有了很多重复项,这就变得方式更有效率了。
对于已经有status = 'ACTIVE'
的行,此更新不会更改任何内容,但仍会以全部成本插入新行版本(适用次要例外)。通常,你不想要这个。添加另一个WHERE
条件,如上所示,以避免这种情况并使其更快:
如果定义status
NOT NULL
,您可以简化为:
AND status <> 'ACTIVE';
此查询(与currently accepted answer by Joel不同)不会将NULL值视为相等。 (saleprice, saledate)
的以下两行符合“不同”(虽然看起来与人眼相同):
(123, NULL)
(123, NULL)
还传入唯一索引,几乎在任何其他位置传递,因为根据SQL标准,NULL值不会相等。参见:
OTOH,GROUP BY
,DISTINCT
或DISTINCT ON ()
将NULL值视为相等。根据您要实现的目标使用适当的查询样式。对于任何或所有比较,您仍然可以使用此IS NOT DISTINCT FROM
而不是=
的更快查询来使NULL比较相等。更多:
如果要比较的所有列都是NOT NULL
,那么就没有分歧的余地。
答案 2 :(得分:22)
您的查询的问题在于,当使用GROUP BY子句(您基本上使用distinct时)时,您只能使用分组的列或聚合函数。您不能使用列ID,因为可能存在不同的值。在你的情况下,由于HAVING子句,总是只有一个值,但是大多数RDBMS都不够聪明,无法识别它。
但这应该有效(并且不需要连接):
UPDATE sales
SET status='ACTIVE'
WHERE id IN (
SELECT MIN(id) FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(id) = 1
)
你也可以使用MAX或AVG代替MIN,如果只有一个匹配的行,使用一个返回列值的函数是很重要的。
答案 3 :(得分:1)
我想从一列'GrondOfLucht'中选择不同的值,但它们应按照'sortering'列中给出的顺序排序。我无法使用
获得仅一列的不同值Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering
它还会给列'分拣',因为'GrondOfLucht'和'分拣'不是唯一的,结果将是所有行。
使用GROUP按照'sortering
给出的顺序选择'GrondOfLucht'的记录SELECT GrondOfLucht
FROM dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)
答案 4 :(得分:0)
如果您的DBMS不支持具有多个列的不同是这样的:
select distinct(col1, col2) from table
通常可以安全地执行多重选择:
select distinct * from (select col1, col2 from table ) as x
因为这可以在大多数DBMS上使用,并且由于避免了分组功能,所以预计它比按解决方案分组要快。