代码之家  ›  专栏  ›  技术社区  ›  sheats

如何在多个列上选择distinct?

  •  350
  • sheats  · 技术社区  · 18 年前

    我需要从一个表中检索所有行,其中2列组合在一起都是不同的。所以我要所有没有其他销售发生在同一天,以相同的价格。基于日期和价格的唯一销售将更新为活动状态。

    所以我想:

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

    但我的大脑比这更痛。

    5 回复  |  直到 7 年前
        1
  •  389
  •   Joel Coehoorn    18 年前
    SELECT DISTINCT a,b,c FROM t
    

    粗略地 相当于:

    SELECT a,b,c FROM t GROUP BY a,b,c
    

    通过语法习惯分组是个好主意,因为它更强大。

    对于你的问题,我会这样做:

    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
     )
    
        2
  •  305
  •   Erwin Brandstetter    7 年前

    如果你把迄今为止的答案汇总起来,整理并改进,你就会得到这个更高级的问题:

    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的测试)。

    但这仍然远不是最佳的。使用A 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 Fiddle.

    用于标识行的唯一键

    如果没有表的主键或唯一键( id 在示例中),可以用系统列替换 ctid 就本查询而言(但不用于其他目的):

       AND    s1.ctid <> s.ctid
    

    每个表都应该有一个主键。如果你还没有,就加一个。我建议 serial IDENTITY Postgres 10+中的列。

    相关:

    这个怎么快?

    中的子查询 存在 一旦发现第一个重复,反半连接就可以停止评估(无需进一步查看)。对于几乎没有重复项的基表,这只会稍微提高效率。如果有很多副本,这就变成了 方式 效率更高。

    排除空更新

    如果已经有一些或多个行 status = 'ACTIVE' ,您的更新不会更改任何内容,但仍然以完全成本插入新的行版本(适用较小的例外情况)。通常情况下,你不想要这个。添加另一个 WHERE 如上文所述的情况,使其更快:

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

    AND status <> 'ACTIVE';
    

    空处理中的细微差异

    此查询(与 currently accepted answer by Joel )不将空值视为相等。这两排是为了 (saleprice, saledate) 将被定义为“独特的”(尽管看起来与人眼相同):

    (123, NULL)
    (123, NULL)
    

    还传入唯一索引和几乎任何其他地方,因为根据SQL标准,空值不相等。见:

    奥托什 GROUP BY DISTINCT DISTINCT ON () 将空值视为相等。根据要实现的目标,使用适当的查询样式。您仍然可以使用这个更快的查询样式 IS NOT DISTINCT FROM 而不是 = 对于任何或所有比较,使空比较相等。更多:

    如果定义了所有要比较的列 非空 没有分歧的余地。

        3
  •  22
  •   Christian Berg    18 年前

    查询的问题是,当使用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,只有在只有一个匹配行的情况下,才使用返回列值的函数。

        4
  •  1
  •   frans eilering    8 年前

    我想从一列“grondoflucht”中选择不同的值,但它们应该按照“排序”列中给出的顺序排序。我无法使用

    Select distinct GrondOfLucht,sortering
    from CorWijzeVanAanleg
    order by sortering
    

    它还将给出“排序”列,因为“grondoflucht”和“sorting”不是唯一的,所以结果将是所有行。

    使用组按“排序”给定的顺序选择“grondoflucht”的记录

    SELECT        GrondOfLucht
    FROM            dbo.CorWijzeVanAanleg
    GROUP BY GrondOfLucht, sortering
    ORDER BY MIN(sortering)
    
        5
  •  0
  •   Abdulhafeth Sartawi    7 年前

    如果您的DBMS不支持这样的多列的distinct:

    select distinct(col1, col2) from table
    

    通常可以安全地执行多重选择,如下所示:

    select distinct * from (select col1, col2 from table ) as x
    

    因为这可以在大多数DBMS上工作,而且由于避免了分组功能,因此预计这比按组解决方案更快。