GROUP BY和DISTINCT之间有什么区别吗?

Bre*_*ski 291 sql group-by distinct

前几天我学到了一些关于SQL的简单知识:

SELECT c FROM myTbl GROUP BY C
Run Code Online (Sandbox Code Playgroud)

结果与:

SELECT DISTINCT C FROM myTbl
Run Code Online (Sandbox Code Playgroud)

我很好奇,SQL引擎处理命令的方式有什么不同,还是它们真的是同一个东西?

我个人更喜欢不同的语法,但我相信它更多地出于习惯而不是其他任何东西.

编辑:这不是关于聚合的问题.的使用GROUP BY与聚合函数了解.

Ske*_*lan 230

就所陈述的问题而言,MusiGenesis的回答在功能上是正确的; SQL Server足够智能,可以意识到如果你使用"分组依据"并且不使用任何聚合函数,那么你实际意味着什么是"区别" - 因此它会生成一个执行计划,就像你只是简单地使用"Distinct"一样".

然而,我认为重要的是要注意汉克的回应 - 如果你不小心的话,对"Group By"和"Distinct"的骑士待遇可能导致一些有害的陷阱.说这不是关于聚合的问题并不完全正确,因为你问的是两个SQL查询关键字之间的功能差异,其中一个用于聚合,而其中一个不是.

有时锤子可以用螺丝钉驱动,但如果你有方便的螺丝刀,为什么要费心呢?

(出于这个类比的目的,Hammer : Screwdriver :: GroupBy : Distinctscrew => get list of unique values in a table column)

  • 感谢我的乐队的新名字:The Pernicious Gotchas. (198认同)
  • 在我曾经使用过的所有“螺丝”定义中,**“获取表列中唯一值的列表”**不是其中之一。 (2认同)

And*_*isi 133

GROUP BY让您使用聚合函数,如AVG,MAX,MIN,SUM,和COUNT.另一只手DISTINCT只是删除重复.

例如,如果您有一堆购买记录,并且您想知道每个部门花了多少钱,您可能会执行以下操作:

SELECT department, SUM(amount) FROM purchases GROUP BY department
Run Code Online (Sandbox Code Playgroud)

这将为每个部门提供一行,包含部门名称以及该部门amount所有行中所有值的总和.

  • 我理解使用GROUP BY,这个问题是基于以下事实:当不存在聚合函数时,它将返回一个不同的数据集。 (2认同)
  • 因为GROUP BY隐式地对你要分组的列的值进行了DISTINCT(抱歉为这个杂音). (2认同)

Luk*_*der 35

仅仅是重复删除功能的观点有什么不同

除了不同的事实DISTINCT,GROUP BY允许每组聚合数据(许多其他答案已经提到),我认为最重要的差异是这两个操作在逻辑顺序的SELECT两个非常不同的步骤"发生"的事实语句中执行的操作.

以下是最重要的操作:

  • FROM(包括JOIN,APPLY等)
  • WHERE
  • GROUP BY (可以删除重复)
  • 聚合
  • HAVING
  • 窗口功能
  • SELECT
  • DISTINCT (可以删除重复)
  • UNION,INTERSECT,EXCEPT (可删除重复)
  • ORDER BY
  • OFFSET
  • LIMIT

正如您所看到的,每个操作的逻辑顺序会影响可以执行的操作以及它如何影响后续操作.特别是,该事实GROUP BY操作"之前发生"SELECT操作(投影)表示:

  1. 它不依赖于投影(这可能是一个优势)
  2. 它不能使用投影中的任何值(这可能是一个缺点)

1.它不依赖于预测

不依赖于投影的示例很有用,如果要在不同的值上计算窗口函数:

SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film
GROUP BY rating
Run Code Online (Sandbox Code Playgroud)

当针对Sakila数据库运行时,会产生:

rating   rn
-----------
G        1
NC-17    2
PG       3
PG-13    4
R        5
Run Code Online (Sandbox Code Playgroud)

DISTINCT轻松实现同样的目标:

SELECT DISTINCT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film
Run Code Online (Sandbox Code Playgroud)

该查询是"错误的"并产生如下内容:

rating   rn
------------
G        1
G        2
G        3
...
G        178
NC-17    179
NC-17    180
...
Run Code Online (Sandbox Code Playgroud)

这不是我们想要的.该DISTINCT操作"之后发生的"投影,这样我们就可以不再删除DISTINCT评级,因为窗函数已经计算和预测.为了使用DISTINCT,我们必须嵌套查询的那一部分:

SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM (
  SELECT DISTINCT rating FROM film
) f
Run Code Online (Sandbox Code Playgroud)

旁注:在这种特殊情况下,我们也可以使用DENSE_RANK()

SELECT DISTINCT rating, dense_rank() OVER (ORDER BY rating) AS rn
FROM film
Run Code Online (Sandbox Code Playgroud)

2.它不能使用投影中的任何值

SQL的一个缺点是它的冗长有时.出于与我们之前看到的相同的原因(即操作的逻辑顺序),我们不能"轻易地"按照我们预测的内容进行分组.

这是无效的SQL:

SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY name
Run Code Online (Sandbox Code Playgroud)

这是有效的(重复表达式)

SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY first_name || ' ' || last_name
Run Code Online (Sandbox Code Playgroud)

这也是有效的(嵌套表达式)

SELECT name
FROM (
  SELECT first_name || ' ' || last_name AS name
  FROM customer
) c
GROUP BY name
Run Code Online (Sandbox Code Playgroud)

我在博文中更深入地介绍了这个主题

  • 老实说,我很惊讶地发现这个问题没有立即讨论执行顺序。谢谢你,解释得也很好。关于你的观点2。一些(一个?)数据库确实允许在整个查询中使用选择别名(我知道的是Teradata,但它是一个例外)。 (4认同)

jkr*_*mer 32

使用DISTINCT,如果你只是想删除重复.使用GROUPY BY,如果你想将集合运算符(MAX,SUM,GROUP_CONCAT,...,或HAVING条款).


Dav*_*sta 19

我预计他们的执行可能存在微妙的差异.我在Oracle 10g中检查了这两行中两个功能相同的查询的执行计划:

core> select sta from zip group by sta;

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    58 |   174 |    44  (19)| 00:00:01 |
|   1 |  HASH GROUP BY     |      |    58 |   174 |    44  (19)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| ZIP  | 42303 |   123K|    38   (6)| 00:00:01 |
---------------------------------------------------------------------------

core> select distinct sta from zip;

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    58 |   174 |    44  (19)| 00:00:01 |
|   1 |  HASH UNIQUE       |      |    58 |   174 |    44  (19)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| ZIP  | 42303 |   123K|    38   (6)| 00:00:01 |
---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

中间操作略有不同:"HASH GROUP BY"与"HASH UNIQUE",但估计的成本等是相同的.然后我通过跟踪执行这些并且两者的实际操作计数是相同的(除了第二个因为缓存而不必进行任何物理读取).

但我认为,因为操作名称不同,执行将遵循一些不同的代码路径,这可能会产生更大的差异.

我认为你应该更喜欢DISTINCT语法.这不仅仅是习惯,它更清楚地表明了查询的目的.


Joe*_*orn 14

对于您发布的查询,它们是相同的.但对于其他可能不正确的查询.

例如,它与以下内容不同:

SELECT C FROM myTbl GROUP BY C, D
Run Code Online (Sandbox Code Playgroud)


The*_*ght 13

我阅读了上述所有评论,但没有看到任何人指出除了聚合位之外Group By和Distinct之间的主要区别.

因为他们是由算法逐个读取不同返回所有的行,然后去复制他们,而集团通过去重复数据删除的行.

这意味着他们可以产生不同的结果!

例如,以下代码会生成不同的结果:

SELECT distinct ROW_NUMBER() OVER (ORDER BY Name), Name FROM NamesTable

 SELECT ROW_NUMBER() OVER (ORDER BY Name), Name FROM NamesTable
GROUP BY Name
Run Code Online (Sandbox Code Playgroud)

如果表中有10个名称,其中1个是另一个的副本,则第一个查询返回10行,而第二个查询返回9行.

原因就是我上面所说的,所以他们可以表现得不同!

  • 那是因为虽然你只是在第二个查询中按`Name`分组,但`distinct`关键字适用于第一个查询的`select`子句中的`Name`和你的`ROW_NUMBER()`列.如果您还按第二个查询中的第一列分组,则查询将返回相同的结果. (11认同)

Bil*_*ard 12

如果对多列使用DISTINCT,则不会将结果集按GROUP BY分组,也不能将聚合函数与DISTINCT一起使用.


Han*_*Gay 11

它们具有不同的语义,即使它们碰巧在您的特定数据上具有相同的结果.


And*_*ter 6

当你的意思是DISTINCT时,请不要使用GROUP BY,即使它们恰好相同.我假设你正试图从查询中减少毫秒数,我必须指出开发人员的时间比计算机时间贵几个数量级.


Dan*_*Dan 5

GROUP BY具有非常特定的含义,与DISTINCT函数不同(heh).

GROUP BY使查询结果使用所选表达式进行分组,然后可以应用聚合函数,这些函数将作用于每个组,而不是整个结果集.

这是一个可能有用的示例:

给定一个如下所示的表:

name
------
barry
dave
bill
dave
dave
barry
john
Run Code Online (Sandbox Code Playgroud)

这个查询:

SELECT name, count(*) AS count FROM table GROUP BY name;
Run Code Online (Sandbox Code Playgroud)

会产生这样的输出:

name    count
-------------
barry   2
dave    3
bill    1
john    1
Run Code Online (Sandbox Code Playgroud)

这显然与使用DISTINCT非常不同.如果要对结果进行分组,请使用GROUP BY,如果只需要特定列的唯一列表,请使用DISTINCT.这将使您的数据库有机会根据您的需求优化查询.


小智 5

如果您在没有任何聚合函数的情况下使用GROUP BY,那么在内部它将被视为DISTINCT,因此在这种情况下,GROUP BY和DISTINCT之间没有区别.

但是,如果为您提供DISTINCT子句更好地使用它来查找您的唯一记录,因为GROUP BY的目标是实现聚合.