我需要从表中检索所有行,其中两列组合起来都不同。因此,我希望同一天没有任何其他销售的所有销售都以相同的价格进行。基于日期和价格的唯一销售将更新为活动状态。
所以我在想:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
但是再走得更远我的大脑就会受伤。
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
)
如果您将到目前为止的答案放在一起,进行清理和改进,您将得到这个更好的查询:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 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
如果您没有表的主键或唯一键(示例中的
id
),您可以使用系统列 ctid
和 tableoid
来替换以用于此查询(但不适用于其他某些查询)目的):
AND (s1.ctid <> s.ctid OR s1.tableoid <> s.tableoid)
tableoid
确保它即使在继承或分区的情况下也能正常工作。参见:
每个表都应该有一个主键。如果您还没有,请添加一个。我建议在 Postgres 10+ 中使用
serial
或 IDENTITY
列。
相关:
EXISTS
反半连接中的子查询一旦找到第一个重复项就可以停止评估(没有必要继续寻找)。对于具有很少重复项的基表,这只会稍微提高效率。有了很多重复项,这就会变得方式更加高效。
对于已经具有
status = 'ACTIVE'
的行,此更新不会改变任何内容,但仍然以(大部分)全额成本插入新的行版本。通常,您不希望这样。添加另一个像上面演示的 WHERE
条件以避免这种情况并使其更快:
如果定义了
status
NOT NULL
,则可以简化为:
AND status <> 'ACTIVE';
列的数据类型必须支持
<>
运算符。有些类型如 json
则不然。参见:
与当前接受的答案不同,此查询不会将 NULL 值视为相等。以下两行
(saleprice, saledate)
将被视为“不同”(尽管与人眼看起来相同):
(123, NULL)
(123, NULL)
还传入唯一索引以及几乎任何其他地方,因为根据 SQL 标准,NULL 值比较不相等。参见:
OTOH、
GROUP BY
、DISTINCT
或 DISTINCT ON ()
将 NULL 值视为相等。根据您想要实现的目标,使用适当的查询样式。您仍然可以使用这个更快的查询,使用 IS NOT DISTINCT FROM
而不是 =
进行任何或所有比较,以使 NULL 比较相等。更多:
如果要比较的所有列都已定义
NOT NULL
,则没有分歧的余地。
查询的问题在于,当使用 GROUP BY 子句(本质上是通过使用 unique 来完成)时,您只能使用分组依据或聚合函数的列。您不能使用列 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,只有在只有一个匹配行时才使用返回列值的函数才重要。
如果您的 DBMS 不支持多列的不同,如下所示:
select distinct(col1, col2) from table
多选一般可以安全地执行,如下所示:
select distinct * from (select col1, col2 from table ) as x
因为这可以在大多数 DBMS 上工作,并且由于您避免了分组功能,因此预计会比分组解决方案更快。
我想从“GrondOfLucht”一列中选择不同的值,但它们应该按照“排序”列中给出的顺序排序。我无法使用
仅获取一列的不同值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)