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

SELECT c FROM myTbl GROUP BY C

结果与:

SELECT DISTINCT C FROM myTbl

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

我个人更喜欢独特的语法,但我相信这更多是出于习惯而不是其他原因。

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


当前回答

在Hive (HQL)中,GROUP BY可以比DISTINCT快得多,因为前者不需要比较表中的所有字段。

参见:https://sqlperformance.com/2017/01/t-sql-queries/surprises-assumptions-group-by-distinct。

其他回答

我对它的理解是,使用distinct就相当于按照所选字段的顺序对它们进行分组。

i.e:

select distinct a, b, c from table;

等于:

select a, b, c from table group by a, b, c

当你指的是DISTINCT时,请不要使用GROUP BY,即使它们的工作方式是一样的。我假设您正在尝试从查询中减少毫秒数,我必须指出,开发人员的时间比计算机的时间要昂贵几个数量级。

我预计在执行上可能会有细微的差异。 我检查了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 |
---------------------------------------------------------------------------

中间的操作略有不同:“HASH GROUP BY”和。“HASH唯一”,但估计成本等是相同的。然后,我在跟踪的情况下执行这些操作,两者的实际操作计数是相同的(除了第二个操作由于缓存而不需要进行任何物理读取)。

但我认为,由于操作名称不同,执行将遵循不同的代码路径,这可能会带来更大的差异。

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

GROUP BY允许您使用聚合函数,如AVG、MAX、MIN、SUM和COUNT。 另一方面,DISTINCT只是删除重复项。

例如,如果你有一堆购买记录,你想知道每个部门花了多少钱,你可能会这样做:

SELECT department, SUM(amount) FROM purchases GROUP BY department

这将为每个部门提供一行,其中包含部门名称和该部门所有行中所有金额值的总和。

仅仅从重复删除功能的角度来看有什么不同

除了与DISTINCT不同的事实,GROUP BY允许每个组聚合数据(这已经被许多其他答案提到),在我看来最重要的区别是,这两个操作“发生”在SELECT语句中执行的操作的逻辑顺序中的两个非常不同的步骤。

以下是最重要的操作:

FROM(包括JOIN、APPLY等) 在哪里 GROUP BY(可以删除重复项) 聚合 有 窗口函数 选择 DISTINCT(可以删除重复项) UNION, INTERSECT, EXCEPT(可删除重复项) 命令 抵消 限制

如您所见,每个操作的逻辑顺序影响着可以用它做什么以及它如何影响后续操作。特别地,GROUP BY操作“发生在”SELECT操作(投影)之前的事实意味着:

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

1. 它不依赖于投影

一个不依赖于投影的例子是,如果你想计算不同值的窗口函数:

SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film
GROUP BY rating

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

rating   rn
-----------
G        1
NC-17    2
PG       3
PG-13    4
R        5

DISTINCT很难做到这一点:

SELECT DISTINCT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film

这个查询是“错误的”,结果如下:

rating   rn
------------
G        1
G        2
G        3
...
G        178
NC-17    179
NC-17    180
...

这不是我们想要的。DISTINCT操作“发生在”投影之后,因此我们不能再删除DISTINCT评级,因为窗口函数已经计算和投影了。为了使用DISTINCT,我们必须嵌套查询的这一部分:

SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM (
  SELECT DISTINCT rating FROM film
) f

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

SELECT DISTINCT rating, dense_rank() OVER (ORDER BY rating) AS rn
FROM film

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

SQL的缺点之一是有时冗长。出于与我们之前看到的相同的原因(即操作的逻辑顺序),我们不能“轻松地”根据我们所投射的东西进行分组。

这是无效的SQL:

SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY name

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

SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY first_name || ' ' || last_name

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

SELECT name
FROM (
  SELECT first_name || ' ' || last_name AS name
  FROM customer
) c
GROUP BY name

我曾在一篇博客文章中更深入地讨论过这个话题