没有聚集函数的GROUP BY子句将如何执行?
在SQL中,GROUP BY子句常用来根据一个或多个列对表进行分组。通常,分组时还需要使用聚合函数,如SUM、AVG、COUNT等,来对每个组内的数据进行计算。但是,如果GROUP BY子句中没有聚合函数,又会发生怎样的情况呢?本文将对此进行详细讲解。
阅读更多:MySQL 教程
GROUP BY子句基本用法
在介绍不带聚合函数的GROUP BY子句的执行方式之前,我们先简单介绍一下GROUP BY子句的基本用法:
SELECT column_name, COUNT(*)
FROM table_name
GROUP BY column_name;
以上语句是一个典型的GROUP BY子句,它将根据column_name
列对table_name
表进行分组,然后统计每个组内的行数。在GROUP BY子句中,聚合函数通常都会作为SELECT语句的一部分使用。
不带聚合函数的GROUP BY子句
如果GROUP BY子句中不包含任何聚合函数,SQL引擎会采用以下方式执行:
- 按照GROUP BY子句中指定的列进行分组;
- 对于每个分组,保留其中的第一行数据,并将其作为结果集的一部分返回。
例如,假设我们有如下表students
:
+----+------+-------+
| id | name | score |
+----+------+-------+
| 1 | Tom | 80 |
| 2 | Bob | 90 |
| 3 | Jim | 60 |
| 4 | Tim | 70 |
| 5 | Ann | 85 |
| 6 | Sam | 95 |
+----+------+-------+
如果我们执行以下语句:
SELECT name, score
FROM students
GROUP BY name;
则结果集将是:
+------+-------+
| name | score |
+------+-------+
| Ann | 85 |
| Bob | 90 |
| Jim | 60 |
| Sam | 95 |
| Tim | 70 |
| Tom | 80 |
+------+-------+
在上面的例子中,我们选择了name
和score
两列,但GROUP BY子句只包含了name
列,没有任何聚合函数。执行结果将会按照name
列的值进行分组,然后对于分组中的每个值,只会选择其中的第一行数据(即相同name
值的第一行数据)。最终结果集中只包含了6行数据,与原表的行数相等,说明没有进行聚合操作。
需要注意的是,SQL标准规定,在不带聚合函数的GROUP BY子句中,只允许出现SELECT列表中的列名、常量和表达式。如果GROUP BY子句中出现了其他列名,将会引发错误。
UNION ALL和ORDER BY
在不带聚合函数的GROUP BY子句中,还有两个和GROUP BY语句相关的操作:UNION ALL 和 ORDER BY 。
UNION ALL
在两个或多个表中,执行类似GROUP BY子句的操作,并将结果合并到一个结果集中。
假设我们有另一个表teacher
,存放了各个科目的任课教师信息:
+------+-------+
| sub | name |
+------+-------+
| Math | John |
| Math | Cathy |
| Math | Jack |
| English | Mike |
| English | Susan|
+------+-------+
我们可以将students
和teacher
的数据进行合并,并根据name
字段进行分组,同时保留各自的科目信息:
SELECT name, 'student' AS type, score
FROMstudents
GROUP BY name
UNION ALL
SELECT name, 'teacher' AS type, NULL AS score
FROM teacher
GROUP BY name;
结果如下所示:
+-------+-----------+-------+
| name | type | score |
+-------+-----------+-------+
| Ann | student | 85 |
| Bob | student | 90 |
| Cathy | teacher | NULL |
| Jack | teacher | NULL |
| Jim | student | 60 |
| John | teacher | NULL |
| Mike | teacher | NULL |
| Sam | student | 95 |
| Susan | teacher | NULL |
| Tim | student | 70 |
| Tom | student | 80 |
+-------+-----------+-------+
这里我们使用了UNION ALL 操作将students
和teacher
两个表中的数据合并成了一个结果集,并使用了一个新的列type
来区分数据是来自哪个表的。请注意,由于teacher
表中没有score
字段,因此我们需要在SELECT
语句中为该列指定NULL
值,以保证两个结果集的列数相同。
ORDER BY
在执行GROUP BY子句后,我们还可以对结果进行排序。例如,我们可以按照分数从高到低对结果进行排序:
SELECT name, score
FROM students
GROUP BY name
ORDER BY score DESC;
结果如下所示:
+------+-------+
| name | score |
+------+-------+
| Sam | 95 |
| Bob | 90 |
| Ann | 85 |
| Tom | 80 |
| Tim | 70 |
| Jim | 60 |
+------+-------+
需要注意的是,由于在GROUP BY子句中没有使用任何聚合函数,因此排序操作实际上是基于每个分组中的第一行数据进行的。因此,结果集的排序方式可能无法满足我们的需求,是需要根据实际情况进行调整的。
结论
当GROUP BY子句中没有聚合函数时,SQL引擎会根据指定的列进行分组,并以每个分组中的第一行数据作为结果集的一部分返回。此外,还可以通过UNION ALL操作合并数据,并通过ORDER BY对结果进行排序。由于没有聚合函数的参与,因此GROUP BY子句查询结果应尽量避免重复、冗余数据的产生。