【问题标题】:DISTINCT for only one column仅一列的 DISTINCT
【发布时间】:2011-06-28 15:30:42
【问题描述】:

假设我有以下查询。

SELECT ID, Email, ProductName, ProductModel FROM Products

如何修改它以使其不返回重复的电子邮件?

换句话说,当多行包含相同的电子邮件时,我希望结果只包含其中一行(最好是最后一行)。应该允许在其他列中重复。

DISTINCTGROUP BY 之类的子句似乎适用于整行。所以我不确定如何解决这个问题。

【问题讨论】:

  • 好的,您需要使用 PARTITION 还是使用两个 select 语句?
  • 如果有 2 行具有相同的电子邮件但产品名称不同,应该显示什么? (最好是最后一个) 不清楚。最后按哪个排序?
  • @ypercube 如问题所述,最好是最后一个。但是,这对我来说并不重要。我只想要其中一个。
  • 你可以看看下面的问题:question1question2或者question3
  • 为什么不能使用:SELECT DISTINCT Email、ID、ProductName、ProductModel FROM Products?

标签: sql sql-server


【解决方案1】:

如果您使用的是 SQL Server 2005 或更高版本,请使用:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
              ) a
WHERE rn = 1

编辑: 使用 where 子句的示例:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
                   WHERE ProductModel = 2
                     AND ProductName LIKE 'CYBER%'

              ) a
WHERE rn = 1

【讨论】:

  • 我必须调查这个 PARTITION 子句,以前从未见过它在起作用。感谢您的示例
  • @Cyber​​nate 一个并发症:我内心的SELECT 需要WHERE 条件。我认为行号将分配给表中的所有行。这种语法有点超出我的想象。是否有任何更新可以保证一行具有满足WHERE 条件的特定电子邮件?
  • 可以在内部sql中加入where子句。一旦我可以访问我的笔记本电脑,我将更新帖子
  • 使用 where 子句使用示例更新了帖子。
  • 只有当我的查询中有 no JOINs 时,我才能正常工作。只要我有JOINROW_NUMBER 就会返回比“1”高得多的值。
【解决方案2】:

DISTINCTGROUP BY 处理整行的原因是您的查询返回整行。

为了帮助您理解:尝试手动写出查询应该返回的内容,您会发现在非重复列中放入什么内容是模棱两可的。

如果您真的不在乎其他列中的内容,请不要返回它们。为每个电子邮件地址返回一个随机行对我来说似乎有点没用。

【讨论】:

  • @JohnFix 我想​​返回整行。我只是不希望当结果已经在电子邮件列中包含具有相同值的行时返回行。
  • 那么它应该如何决定返回哪一个呢?您真的想要一个为每封电子邮件返回任意行的查询吗?这真的闻起来像您可能需要重新考虑您要解决的问题。几乎每次我都被问到这个问题(而且出现了很多),结果证明开发人员没有考虑到应用程序中这种行为的后果。
  • 我真的很难理解你的逻辑。如问题所述,我更喜欢最后一个(按ID排序)。是的,如果它选择了一个随机行就可以了。而且,是的,我已经考虑过了。
【解决方案3】:

这假设 SQL Server 2005+ 并且您对“最后一个”的定义是给定电子邮件的最大 PK

WITH CTE AS
(
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel, 
       ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC) AS RowNumber 
FROM   Products
)
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel
FROM CTE 
WHERE RowNumber = 1

【讨论】:

    【解决方案4】:

    当您使用DISTINCT 时,请将其视为不同的行,而不是列。它只会返回列不完全匹配的行。

    SELECT DISTINCT ID, Email, ProductName, ProductModel
    FROM Products
    
    ----------------------
    1 | something@something.com | ProductName1 | ProductModel1
    2 | something@something.com | ProductName1 | ProductModel1
    

    查询将返回两行,因为 ID 列不同。我假设ID 列是一个递增的IDENTITY 列,如果你想返回最后一个,那么我建议这样:

    SELECT DISTINCT TOP 1 ID, Email, ProductName, ProductModel
    FROM Products
    ORDER BY ID DESC
    

    TOP 1 将只返回第一条记录,通过将其按ID 降序排序,它将首先返回最后一行的结果。这将为您提供最后一条记录。

    【讨论】:

    • 如问题中所述,我看到 DISTINCT 对整行都有效。我想按照你上面的建议做,但每次电子邮件在结果中重复(不仅仅是一次)。
    • 在这种情况下,我建议使用@Cyber​​nate 答案。这应该完全符合您的需要。
    【解决方案5】:

    对于 Access,您可以使用我在此处介绍的 SQL Select 查询:

    例如你有这张桌子:

    客户||名词 ||邮件

    888 || T800 阿诺德 || t800.arnold@cyberdyne.com

    123 ||约翰·康纳 || s.connor@skynet.com

    125 ||莎拉·康纳 ||s.connor@skynet.com

    您只需要选择不同的邮件。 你可以这样做:

    SQL 选择:

    SELECT MAX(p.CLIENTE) AS ID_CLIENTE
    , (SELECT TOP 1 x.NOMBRES 
        FROM Rep_Pre_Ene_MUESTRA AS x 
        WHERE x.MAIL=p.MAIL 
         AND x.CLIENTE=(SELECT MAX(l.CLIENTE) FROM Rep_Pre_Ene_MUESTRA AS l WHERE x.MAIL=l.MAIL)) AS NOMBRE, 
    p.MAIL
    FROM Rep_Pre_Ene_MUESTRA AS p
    GROUP BY p.MAIL;
    

    您可以使用它来选择最大 ID,该最大 ID 的对应名称,您可以通过这种方式添加任何其他属性。然后最后将不同的列放在过滤器中,并且只将它与最后一个不同的列分组。

    这将为您带来对应数据的最大 ID,您可以使用 min 或任何其他函数并将该函数复制到子查询。

    此选择将返回:

    客户||名词 ||邮件

    888 || T800 阿诺德 || t800.arnold@cyberdyne.com

    125 ||莎拉·康纳 ||s.connor@skynet.com

    请记住为您选择的列编制索引,并且不同列的数字数据不得全部为大写或小写,否则将不起作用。 这也仅适用于一封挂号邮件。 编码愉快!!!

    【讨论】:

      【解决方案6】:

      您可以像这样使用GROUP BY 来解决这个问题:

      SELECT ID, Email, ProductName, ProductModel
      FROM Products
      GROUP BY Email
      

      【讨论】:

      • 列 'Products.ID' 在选择列表中无效,因为它不包含在聚合函数或 GROUP BY 子句中。
      • 如果不对其他列使用诸如 MAX(ID)、MAX(ProductName)、MAX(ProductModel) 之类的内容,这将不起作用
      • 在 postgres 中,您只需要在 group by 子句中使用的列上的聚合函数,例如SELECT id, max(email) AS email FROM tbl GROUP by email。在 SQL Server 中,SELECT 子句中的所有列都必须位于聚合函数中。每次回去都会咬我。
      【解决方案7】:

      试试这个

      ;With Tab AS (SELECT DISTINCT Email FROM  Products)
      SELECT Email,ROW_NUMBER() OVER(ORDER BY Email ASC) AS  Id FROM Tab
      ORDER BY Email ASC
      

      【讨论】:

        【解决方案8】:

        试试这个:

        SELECT ID, Email, ProductName, ProductModel FROM Products WHERE ID IN (SELECT MAX(ID) FROM Products GROUP BY Email)
        

        【讨论】:

        • 我们为什么要试试这个?为什么这比过去 8 年在这里发布的其他答案更好?如果您想分享更好的解决问题的方法,您需要说明您推荐它的原因。
        猜你喜欢
        • 1970-01-01
        • 2012-08-09
        • 2019-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-28
        相关资源
        最近更新 更多