SQL-在分组依据中使用别名


143

只是对SQL语法感到好奇。所以如果我有

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY itemName, FirstLetter

这将是不正确的,因为

GROUP BY itemName, FirstLetter 

真的应该是

GROUP BY itemName, substring(itemName, 1,1)

但是为什么我们不能简单地使用前者来方便呢?


13
Postgresql中允许这样做
Michael Buen

7
MySQL也允许
Kip

1
您正在谈论哪个rdbms?
Shiwangini

Answers:


292

就像按以下顺序执行查询一样实现SQL:

  1. FROM子句
  2. WHERE子句
  3. GROUP BY子句
  4. HAVING子句
  5. SELECT子句
  6. ORDER BY子句

对于大多数关系数据库系统,此顺序说明哪些名称(列或别名)有效,因为它们必须在上一步中引入。

因此,在Oracle和SQL Server中,您不能在SELECT子句中定义的GROUP BY子句中使用术语,因为GROUP BY是在SELECT子句之前执行的。

但是也有例外:MySQL和Postgres似乎还具有其他一些聪明之处。


3
我喜欢这个解释。尽管我无法推测将其作为语法糖添加到引擎有多么困难。
2010年

11
知道DB是否足够聪明以实现相同的表达式时,可以在SELECT和GROUP BY子句中进行,而无需重新评估表达式?也就是说,如果存在GROUP BY substring(itemName, 1,1),那么数据库是否足够聪明,以至于不会影响重新计算SELECT子句中子字符串的性能?
Kip

10
在具有分组的查询的SELECT子句中,您只能访问GROUP BY表达式和聚合值。因此,这不是要变得聪明。必须采用这种方式来实现分组工作。(这是SQL标准所必需的)。但是,即使在更琐碎的情况下(例如WHERE和SELECT子句中的相同表达式),最新的数据库系统也肯定只会计算一次。这种优化称为通用子表达式消除
2013年

6
执行顺序与该问题有什么关系?这并不像问问者试图对COUNT()进行GROUP BY那样。实际上,如注释中指出的那样,所查询的查询在MySQL和PostgreSQL中都可以正常工作。

1
对于MySQL,sql_mode不包括ONLY_FULL_GROUP_BY位掩码中,优化有机会提供更好的结果有变化/ 不同的使用别名的HAVING条款。
提请

28

您可以始终使用子查询,以便可以使用别名。当然,请检查性能(可能db服务器将以相同的方式运行,但无需担心进行验证):

SELECT ItemName, FirstLetter, COUNT(ItemName)
FROM (
    SELECT ItemName, SUBSTRING(ItemName, 1, 1) AS FirstLetter
    FROM table1
    ) ItemNames
GROUP BY ItemName, FirstLetter

2
由于性能不佳,应尽可能避免子查询。使用该函数的副本要好得多,因为它当然可以由数据库优化程序检测到并且仅执行一次。
罗兰

1
@Roland,但是在那种情况下执行计划没有什么不同。还有其他性能方面的考虑吗?
Guido Mocha

@Roland,相关子查询或其他语法导致循环或行由行的行为应该避免,而且有你应该嵌套子查询去有多深的限制,但它通常是不正确的是带领子查询表现不佳。就像Chris所说的,在这种情况下,您可以比较有无子查询的执行计划(AKA查询计划,解释计划),并查看它们之间是否真的有任何区别。几乎每个数据库引擎都会重新编写您的查询,因此您无法完全控制执行的内容。这就是声明性语法的重点。
达沃斯

16

至少在PostgreSQL中,您可以在GROUP BY子句的结果集中使用列号:

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY 1, 2

当然,如果您以交互方式进行此操作,并且编辑查询以更改结果中的列数或列顺序,那么这会很麻烦。但是还是。


GROUP BY FirstLetter在Postgresql中是允许的。明智的做法是尝试在Postgresql中运行它:从subname(table_name,1,2)中选择tname的信息,并按tname分组
迈克尔·布恩

1
@MichaelBuen似乎对我可能有问题。从快速测试来看,好像有一个别名和一个具有相同名称的基表列,后者优先吗?SQL小提琴。因此,如果通过别名依赖此组,则以后的模式更改可能会无提示地破坏您的查询并更改语义。
马丁史密斯

@MartinSmith现在只知道这是一个陷阱,请避免使用它,谢谢。鉴于PostgreSQL允许该快捷方式,他们应该给别名一个优先级,否则他们根本不应该允许该快捷方式。
迈克尔·布恩

这是PostgreSQL设计师的一个糟糕主意。尝试使用GROUP BY任何包含聚合函数或窗口函数的表达式时,这会造成混乱,“显然”不起作用。
卢卡斯·埃德

13

由于处理的逻辑顺序,SQL Server不允许您在GROUP BY子句中引用别名。GROUP BY子句在SELECT子句之前处理,因此在评估GROUP BY子句时别名是未知的。这也解释了为什么可以在ORDER BY子句中使用别名。

这是有关SQL Server逻辑处理阶段的信息的一种来源。


8

我没有回答为什么会这样,而是只想通过使用CROSS APPLY创建别名来解决SQL Server中这种限制的方法。然后,您可以在GROUP BY子句中使用它,如下所示:

SELECT 
 itemName as ItemName,
 FirstLetter,
 Count(itemName)
FROM table1
CROSS APPLY (SELECT substring(itemName, 1,1) as FirstLetter) Alias
GROUP BY itemName, FirstLetter

4

警告在Group By中使用别名(对于支持别名的服务,例如postgres)可能会产生意想不到的结果。例如,如果创建一个内部语句中已经存在的别名,则“分组依据”将选择内部字段名称。

-- Working example in postgres
select col1 as col1_1, avg(col3) as col2_1
from
    (select gender as col1, maritalstatus as col2, 
    yearlyincome as col3 from customer) as layer_1
group by col1_1;

-- Failing example in postgres
select col2 as col1, avg(col3)
from
    (select gender as col1, maritalstatus as col2,
    yearlyincome as col3 from customer) as layer_1
group by col1;

3

一些DBMS允许您使用别名,而不必重复整个表达式。
Teradata就是这样一个例子。

由于这个SO问题中记录的原因,我避免使用Bill建议的序号表示法。

一种简单而强大的选择是始终在GROUP BY子句中重复该表达式。
DRY不适用于SQL。


1

从SQLite中的视图对结果进行分组时,请注意不要使用别名。如果别名与任何基础表的列名相同(对于视图),您将得到意外的结果。


0

早在前一天,我发现Rdb(Oracle现在支持的前DEC产品)允许在GROUP BY中使用列别名。主流Oracle到版本11不允许在GROUP BY中使用列别名。不知道哪些Postgresql,SQL Server,MySQL等会允许或不允许。YMMV。

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.