DISTINCT仅一栏


155

假设我有以下查询。

SELECT ID, Email, ProductName, ProductModel FROM Products

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

换句话说,当几行包含同一封电子邮件时,我希望结果仅包含这些行之一(最好是最后一行)。在其他列中应允许重复。

子句喜欢DISTINCT并且GROUP BY似乎在整个行上都起作用。所以我不确定该如何处理。


2
好的,您需要使用PARTITION还是使用两个select语句?
CarneyCode

如果说两行具有相同的电子邮件但产品名称不同,应该显示什么?的(优选地最后一个)不清晰。最后按哪个顺序?
ypercubeᵀᴹ

@ypercube如问题所述,最好是最后一个。但是,这对我来说并不是很关键。我只想要其中之一。
乔纳森·伍德

1
您可以查看以下问题:Question1Question2Question3
玛丽安

您为什么不能使用:从产品中选择DISTINCT电子邮件,ID,ProductName,ProductModel?
里克·亨德森

Answers:


186

如果您使用的是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

4
我必须研究这个PARTITION子句,以前从未见过。感谢您的示例
LorenVS

@Cyber​​nate一个并发症:我的内心SELECT需要一个WHERE条件。我在想将行号分配给表中的所有行。这种语法超出了我一点。是否有可能保证某行符合WHERE条件的特定更新的更新机会?
乔纳森·伍德

1
您可以将where子句添加到内部sql。我将更新后一次我可以访问我的笔记本电脑
Chandu

1
使用where子句使用示例更新了帖子。
Chandu

1
只有在查询中没有 JOIN s 时,我才能正常工作。一旦有了a JOINROW_NUMBER返回的值就比“ 1”高得多。
Uwe Keim

10

假设使用SQL Server 2005+,并且您对“ last”的定义是给定电子邮件的最大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

6

当您使用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下降,会先返回的结果与最后一排。这将给您最后的记录。


2
如问题中所述,我看到DISTINCT适用于整行。我想像您上面建议的那样做,但是对于结果中的每一次电子邮件重复(而不仅仅是一次)。
乔纳森·伍德

在这种情况下,我建议使用@Cyber​​nate答案。那应该完全满足您的需求。
jon3laze 2011年

4

您可以使用GROUP BY函数

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


16
选择列表中的“ Products.ID”列无效,因为它不包含在聚合函数或GROUP BY子句中。
palota

2
如果不为其他列使用MAX(ID),MAX(ProductName),MAX(ProductModel)之类的方法,这将无法正常工作
avl_sweden

2
在postgres中,只需要在group by子句中使用的列上的聚合函数即可,例如SELECT id, max(email) AS email FROM tbl GROUP by email。在SQL Server中,SELECT子句中的ALL列必须在聚合函数中。每当我回去时,这都会刺痛我。
布鲁斯·皮尔森

这将永远行不通。这是一个糟糕的解决方案
Dan AS

1

对于Access,可以使用我在此处显示的SQL Select查询:

例如,您有此表:

客户|| NOMBRES || 邮件

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

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

125 || SARAH CONNOR ||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或任何其他函数,然后将该函数复制到子查询中。

该选择将返回:

客户|| NOMBRES || 邮件

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

125 || SARAH CONNOR ||s.connor@skynet.com

请记住为选定的列建立索引,并且不同的列必须全部没有大写或小写数字数据,否则它将不起作用。这也仅适用于一封已注册的邮件。编码愉快!!!


0

原因DISTINCTGROUP BY在整个行的工作是你的查询返回整行。

为了帮助您理解:尝试手动写出查询应返回的内容,您将发现在非重复列中放置内容是不明确的。

如果您实际上不关心其他列中的内容,请不要返回它们。对每个电子邮件地址返回随机行对我来说似乎没有用。


@JohnFix我想返回整行。当结果已经在“电子邮件”列中包含具有相同值的行时,我只是不希望返回行。
乔纳森·伍德

那么,应该如何决定退回哪一个呢?您是否真的要为每个电子邮件返回任意行的查询。这确实闻起来像您可能需要重新考虑您要解决的问题。几乎每次我被问到这个问题时(很多时候),开发人员都没有考虑过应用程序中这种行为的后果。
JohnFx

6
我真的很难遵循您的逻辑。如问题中所述,我希望使用最后一个(按ID排序)。是的,如果它选择了随机行就可以了。而且,是的,我已经考虑过了。
乔纳森·伍德

0

试试这个

;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

-2

试试这个:

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

2
我们为什么要尝试这个?为什么这比过去8年在这里发布的其他答案更好?如果您想分享解决问题的更好方法,则需要解释为什么推荐它。
Dharman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.