SQL / mysql-选择不重复/唯一,但返回所有列?


373
SELECT DISTINCT field1, field2, field3, ......   FROM table

我正在尝试完成以下sql语句,但我希望它返回所有列,这可能吗?就像是:

SELECT DISTINCT field1, * from table

12
为什么SELECT DISTINCT * FROM table对您不起作用?
ypercubeᵀᴹ

19
如果您的表具有PK,则所有行均应按照distinct定义。如果您只是尝试选择DISTINCT field1但以某种方式返回所有其他列,那么对于一个特定field1值而言,具有多个值的那些列应该怎么办?例如,您将需要GROUP BY在其他列上使用和某种聚合。
马丁·史密斯

1
如果您想要重复的行而不是唯一的行,请删除唯一的关键字。
Hyperboreus

2
您能否举例说明预期结果如何?到目前为止,我无法理解您想要的查询。
递归

3
这是询问的类似问题的答案,您需要首先获取具有其ID的非重复列,然后将其与原始表连接。在一列上选择DISTINCT,返回其他多列
yadavr

Answers:


407

您正在寻找一个分组依据:

select *
from table
group by field1

有时可以用不同的on语句编写:

select distinct on field1 *
from table

但是,在大多数平台上,上述两种方法都不起作用,因为未指定其他列的行为。(如果您正在使用MySQL,那么第一个可以在MySQL中使用。)

您可以获取不同的字段,并坚持每次选择一个任意行。

在某些平台(例如PostgreSQL,Oracle,T-SQL)上,可以直接使用窗口函数来完成此操作:

select *
from (
   select *,
          row_number() over (partition by field1 order by field2) as row_number
   from table
   ) as rows
where row_number = 1

在其他(MySQL,SQLite)上,您需要编写子查询,这些查询将使您将整个表与自身连接(示例),因此不建议这样做。


10
该查询不会为我解析,并给出错误:The ranking function "row_number" must have an ORDER BY clause。我们需要在按field1分区之后添加order by子句。因此正确的查询将是 select * from ( select *, row_number() over (partition by field1 order by orderbyFieldName) as row_number from table ) as rows where row_number = 1
Ankur-m 2012年

1
谢谢!我GROUP BY
遇到

2
同样在Oracle(Oracle SQL Developer)中,您不能指定select *, row_number() over (partition by field1 order by field2) as row_number from table。您必须在选择查询中显式使用表名/别名select **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4 '17

1
@jarlh:今天可能... 您可能会注意到,这个答案已经有将近7年的历史了,在这个时间点上,情况并非如此,因为我可以在运动时从背部回想一下。如果您认为有必要,欢迎您重新标记和/或编辑答案。
Denis de Bernardy

2
select distinct on (field1) * from table; 在PostgreSQL中也可以工作
Chilianu Bogdan

61

从问题的表述中,我了解到您想为给定字段选择不同的值,并为每个此类值选择同一行中列出的所有其他列值。大多数DBMS都不允许使用no DISTINCTGROUP BY,因为结果是不确定的。

可以这样想:如果您field1多次出现,field2将列出的值(假设您field1在两行中具有相同的值,但在这两行中具有两个不同的值field2)。

但是,您可以使用集合函数(对于要显示的每个字段明确使用),并使用GROUP BY代替DISTINCT

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1

4
此解决方案+1。这样我们就可以了SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1,并且field2、3、4,...不必是整数(或其他数字),它们也可以是char字段
跟踪

一直很好,直到我被布尔列卡住了。即使MIN(Dynamic)列的值被更改为false,也可以将其修改为false。其他任何聚合函数都可用于处理布尔值-signonsridhar 6分钟前。总和(动态)更改为1

1
很棒的建议,使我找到了我认为更通用的解决方案-看看吧!
加勒特·辛普森

@signonsridhar将您的布尔值转换为int并使用sum;例如sum(cast(COL as int)) > 0
Drew

26

如果我正确理解了您的问题,则与我刚遇到的问题类似。您希望能够将DISTINCT的可用性限制为指定的字段,而不是将其应用于所有数据。

如果您使用不带汇总功能的GROUP BY,则GROUP BY的哪个字段将是您的DISTINCT字段。

如果您进行查询:

SELECT * from table GROUP BY field1;

它将基于单个field1实例显示所有结果。

例如,如果您有一个包含名称,地址和城市的表。一个人记录了多个地址,但是您只需要一个人的地址,就可以查询如下:

SELECT * FROM persons GROUP BY name;

结果将是该名称的一个实例只会显示其地址,而另一个实例将从结果表中省略。注意:如果您的文件具有原子值,例如firstName,lastName,则希望将两者按组进行分组。

SELECT * FROM persons GROUP BY lastName, firstName;

因为如果两个人的姓氏相同,而您仅按姓氏分组,则结果中将省略其中一个人。您需要考虑这些因素。希望这可以帮助。


正如公认的答案中所提到的,它将适用于大多数SQL版本-仅适用于MYSQL
Garrett Simpson

15
SELECT  c2.field1 ,
        field2
FROM    (SELECT DISTINCT
                field1
         FROM   dbo.TABLE AS C
        ) AS c1
        JOIN dbo.TABLE AS c2 ON c1.field1 = c2.field1

为什么会有C alias没有它的作品?排队FROM dbo.TABLE AS C
塔拉(Talha)

2
我相信这是由于我使用RedGate SQLPrompt。我配置它的方式,它总是添加别名-即使没有必要。那里是“以防万一”
风雨如磐

这对我来说似乎很有希望,但它仍然带回了所有行,而不是唯一的字段1。:(
Michael Fever

13

这是一个非常好的问题。我已经在这里阅读了一些有用的答案,但是也许我可以添加更精确的解释。

只要不查询其他信息,使用GROUP BY语句减少查询结果的数量就很容易。假设您获得了下表“位置”。

--country-- --city--
 France      Lyon
 Poland      Krakow
 France      Paris
 France      Marseille
 Italy       Milano

现在查询

SELECT country FROM locations
GROUP BY country

将导致:

--country--
 France
 Poland
 Italy

但是,以下查询

SELECT country, city FROM locations
GROUP BY country

...在MS SQL中引发错误,因为您的计算机如何知道要在“法国”右侧的字段中读取的三个法国城市“里昂”,“巴黎”或“马赛”中的哪个?

为了更正第二个查询,您必须添加此信息。一种实现方法是使用函数MAX()或MIN(),在所有候选项中选择最大值或最小值。MAX()和MIN()不仅适用于数字值,而且还比较字符串值的字母顺序。

SELECT country, MAX(city) FROM locations
GROUP BY country

将导致:

--country-- --city--
 France      Paris
 Poland      Krakow
 Italy       Milano

要么:

SELECT country, MIN(city) FROM locations
GROUP BY country

将导致:

--country-- --city--
 France      Lyon
 Poland      Krakow
 Italy       Milano

只要您可以从字母(或数字)顺序的两端选择值,这些函数就是一个很好的解决方案。但是,如果不是这种情况怎么办?让我们假设您需要一个具有特定特征的值,例如以字母“ M”开头。现在事情变得复杂了。

到目前为止,我唯一能找到的解决方案是将整个查询放入一个子查询中,并手动构造它之外的其他列:

SELECT
     countrylist.*,
     (SELECT TOP 1 city
     FROM locations
     WHERE
          country = countrylist.country
          AND city like 'M%'
     )
FROM
(SELECT country FROM locations
GROUP BY country) countrylist

将导致:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano

5

很好的问题@aryaxt-您可以说这是一个很好的问题,因为您是5年前问的,而今天我偶然发现了它,试图找到答案!

我只是尝试编辑接受的答案以包括此答案,但是如果我的编辑没有在以下答案中出现:

如果您的表不是那么大,并且假设您的主键是一个自动递增的整数,则可以执行以下操作:

SELECT 
  table.*
FROM table
--be able to take out dupes later
LEFT JOIN (
  SELECT field, MAX(id) as id
  FROM table
  GROUP BY field
) as noDupes on noDupes.id = table.id
WHERE
  //this will result in only the last instance being seen
  noDupes.id is not NULL


3

您可以使用WITH子句来实现。

例如:

WITH c AS (SELECT DISTINCT a, b, c FROM tableName)
SELECT * FROM tableName r, c WHERE c.rowid=r.rowid AND c.a=r.a AND c.b=r.b AND c.c=r.c

这还允许您仅选择在WITH子句查询中选择的行。


2

对于SQL Server,您可以使用density_rank和其他窗口函数来获取所有行和列,这些列在指定列上具有重复的值。这是一个例子

with t as (
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r1' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r2' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r3' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r4' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r5' union all
    select col1 = 'a', col2 = 'a', col3 = 'a', other = 'r6'
), tdr as (
    select 
        *, 
        total_dr_rows = count(*) over(partition by dr)
    from (
        select 
            *, 
            dr = dense_rank() over(order by col1, col2, col3),
            dr_rn = row_number() over(partition by col1, col2, col3 order by other)
        from 
            t
    ) x
)

select * from tdr where total_dr_rows > 1

这将对col1,col2和col3的每个不同组合进行行计数。


过于复杂且特定于SQL的一种实现方式
Garrett Simpson

1
select min(table.id), table.column1
from table 
group by table.column1

这对我有用!值得注意的是,如果您使用的是fetch_array(),则需要通过索引标签调用每一行,而不是隐式调用行名。没有足够的字符让我写出我有:X抱歉的示例!
布兰登·普瑞斯

0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

ORDER BY我刚刚把例子放在这里,您也可以在此添加ID字段


如公认的答案所述,它将适用于大多数SQL版本-仅适用于MYSQL
Garrett Simpson

0

在这里的其他地方找到了这个,但这是一个简单的解决方案,可以工作:

 WITH cte AS /* Declaring a new table named 'cte' to be a clone of your table */
 (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY val1 DESC) AS rn
 FROM MyTable /* Selecting only unique values based on the "id" field */
 )
 SELECT * /* Here you can specify several columns to retrieve */
 FROM cte
 WHERE rn = 1

适用于MSSQL
Michael Fever

-1

将GROUP BY添加到要检查查询重复项的字段中

SELECT field1, field2, field3, ......   FROM table GROUP BY field1

将检查field1以排除重复的记录

或者你可以像这样查询

SELECT *  FROM table GROUP BY field1

SELECT中排除了field1的重复记录


1
GROUP BY子句必须与所选字段匹配。否则它将引发错误,例如filed2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a

-2

只需在GROUP BY子句中包括所有字段即可。


3
为了获得一个好的答案,您应该包括一些关于您的意思的更多细节。
罗伯特

-2

可以通过内部查询来完成

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";

2
这不能回答问题,OP试图获取表的所有数据,但删除包含单个字段重复项的行
Garrett Simpson

-3
SELECT * from table where field in (SELECT distinct field from table)

7
那行不通。您已经在子查询中选择了非重复列,但是where子句使用该值获取所有这些列。因此,查询与写“从表中选择*”一样好,除非“字段”列是唯一列,在这种情况下,根本不需要该列的唯一性。
Ankur-m

-3

如果表中所有三列的值都是唯一的,则SELECT DISTINCT FIELD1,FIELD2,FIELD3 FROM TABLE1起作用。

例如,如果您的名字具有多个相同的值,但是所选列中的姓氏和其他信息不同,那么记录将包含在结果集中。


2
这不能回答问题,OP试图获取表的所有数据,但删除包含单个字段重复项的行
Garrett Simpson

-3

我建议使用

SELECT  * from table where field1 in 
(
  select distinct field1 from table
)

这样,如果您在多行中的field1中具有相同的值,则将返回所有记录。


1
与没什么不同SELECT * FROM table;。甚至更多它很慢。
Shin Kim

请先尝试您的答案。
谢里夫
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.