我如何(或可以)在多个列上选择 DISTINCT?
-
09-06-2019 - |
题
我需要从表中检索所有行,其中两列的组合都不同。因此,我希望同一天没有任何其他销售的所有销售都以相同的价格进行。基于日期和价格的唯一销售将更新为活动状态。
所以我在想:
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
);
这是 很多 比他们任何一个都快。将当前接受的答案的性能提高 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
数据库<>小提琴 这里
旧的 SQL 小提琴
识别行的唯一键
如果您没有表的主键或唯一键(id
在示例中),您可以替换为系统列 ctid
出于此查询的目的(但不出于其他目的):
AND s1.ctid <> s.ctid
每个表都应该有一个主键。如果您还没有,请添加一个。我建议一个 serial
或一个 IDENTITY
Postgres 10+ 中的专栏。
有关的:
这怎么更快?
中的子查询 EXISTS
一旦发现第一个受骗者,反半连接就可以停止评估(进一步寻找没有意义)。对于具有很少重复项的基表,这只是稍微更有效。由于有很多重复项,这变成了 方式 更高效。
排除空更新
对于已经有的行 status = 'ACTIVE'
此更新不会更改任何内容,但仍以全部成本插入新的行版本(存在较小的例外情况)。通常,您不希望这样。加上另一个 WHERE
像上面演示的情况可以避免这种情况并使其更快:
如果 status
被定义为 NOT NULL
, ,您可以简化为:
AND status <> 'ACTIVE';
NULL 处理的细微差别
此查询(与 目前乔尔接受的答案) 不将 NULL 值视为相等。下面两行为 (saleprice, saledate)
将被视为“独特”(尽管看起来与人眼相同):
(123, NULL)
(123, NULL)
还传递唯一索引以及几乎任何其他地方,因为根据 SQL 标准,NULL 值比较不相等。看:
奥托, GROUP BY
, DISTINCT
或者 DISTINCT ON ()
将 NULL 值视为相等。根据您想要实现的目标,使用适当的查询样式。您仍然可以使用这个更快的查询 IS NOT DISTINCT FROM
代替 =
对于任何或所有比较,使 NULL 比较相等。更多的:
如果定义了所有要比较的列 NOT NULL
, ,没有任何异议的余地。
您的查询的问题在于,当使用 GROUP BY 子句(本质上是通过使用不同的)时,您只能使用分组依据或聚合函数的列。您不能使用列 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,只有在只有一个匹配行时才使用返回列值的函数才重要。
我想从“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)
如果您的 DBMS 不支持多列的不同,如下所示:
select distinct(col1, col2) from table
一般来说,多选可以安全地执行,如下所示:
select distinct * from (select col1, col2 from table ) as x
因为这可以在大多数 DBMS 上工作,并且由于您避免了分组功能,因此预计比分组解决方案更快。