我如何(或可以)在多列上选择DISTINCT?

时间:2008-09-10 15:33:11

标签: sql postgresql sql-update duplicates distinct

我需要检索表中的所有行,其中2列组合在一起是不同的。因此,我希望所有在同一天没有任何其他销售的销售以相同的价格出售。基于日期和价格的唯一销售将更新为活动状态。

所以我在想:

UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
             FROM sales
             HAVING count = 1)

但是我的大脑比那更远了。

5 个答案:

答案 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 +中使用serialIDENTITY列。

相关:

这怎么会更快?

EXISTS反半连接中的子查询可以在找到第一个欺骗后立即停止评估(没有必要进一步查看)。对于具有少量重复的基表,这只是稍微更有效。有了很多重复项,这就变得方式更有效率了。

排除空更新

对于已经有status = 'ACTIVE'的行,此更新不会更改任何内容,但仍会以全部成本插入新行版本(适用次要例外)。通常,你不想要这个。添加另一个WHERE条件,如上所示,以避免这种情况并使其更快:

如果定义status NOT NULL,您可以简化为:

AND status <> 'ACTIVE';

NULL处理的细微差别

此查询(与currently accepted answer by Joel不同)不会将NULL值视为相等。 (saleprice, saledate)的以下两行符合“不同”(虽然看起来与人眼相同):

(123, NULL)
(123, NULL)

还传入唯一索引,几乎在任何其他位置传递,因为根据SQL标准,NULL值不会相等。参见:

OTOH,GROUP BYDISTINCTDISTINCT 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上使用,并且由于避免了分组功能,所以预计它比按解决方案分组要快。