深入Group By分组查询

GROUP BY 子句用来为结果集中的每一行产生聚合值。如果聚合函数没有使用 GROUP BY 子句,则只为 SELECT 语句报告一个聚合值。

以下示例返回分类 2 中每种产品已销售的单位数量:

USE Northwind
SELECT OrdD.ProductID AS ProdID,
           SUM(OrdD.Quantity) AS AmountSold
FROM [Order Details] AS OrdD JOIN Products as Prd
         ON OrdD.ProductID = Prd.ProductID
         AND Prd.CategoryID = 2
GROUP BY OrdD.ProductID下面是结果集:

ProdID          AmountSold     
----------- -----------
3               328            
4               453            
5               298            
6               301            
8               372            
15              122            
44              601            
61              603            
63              445            
65              745            
66              239            
77              791            

(12 row(s) affected)GROUP BY 关键字后面跟着列的列表,称为分组列。GROUP BY 子句限制结果集中的行;对于分组列中的每个非重复值只有一行。每个结果集行都包含与其分组列中特定值相关的汇总数据。

当 SELECT 语句中包含 GROUP BY 关键字时,对可以在选择列表中指定的项有一些限制。在该选择列表中所允许的项目是:

分组列。
为分组列中的每个值只返回一个值的表达式,例如将列名作为其中一个参数的聚合函数。这些函数称为矢量聚合。
例如,TableX 包含:

ColumnA ColumnB ColumnC
------- ------- -------
1 abc 5
1 def 4
1 ghi 9
2 jkl 8
2 mno 3


如果 ColumnA 是分组列,则结果集中将有两行,其中一行汇总值 1 的信息,而另一行汇总值 2 的信息。

如果 ColumnA 是分组列,要引用 ColumnB 或 ColumnC,这两列必须能为 ColumnA 中的每个值返回单个值的聚合函数中的参数。选择列表中包含诸如 MAX (ColumnB)、SUM (ColumnC) 或 AVG (ColumnC) 之类的表达式是合法的:

SELECT ColumnA,
           MAX(ColumnB) AS MaxB,
           SUM(ColumnC) AS SumC
FROM TableX
GROUP BY ColumnA该选择语句返回两行,为 ColumnA 中的每个唯一值各返回一行:

ColumnA         MaxB SumC           
----------- ---- -----------
1               ghi      18             
2               mno      11             

(2 row(s) affected)但是,选择列表中只包含 ColumnB 表达式是不合法的:

SELECT ColumnA,
           ColumnB,
           SUM(ColumnC) AS SumC
FROM TableX
GROUP BY ColumnA由于 GROUP BY 关键字只能返回一行,该行 ColumnA 中的值为 1,因此无法返回与 ColumnA 中的值 1 关联的 ColumnB 的三个值(abc、def 和 ghi)。

不能对 ntext、text、image 或 bit 列使用 GROUP BY 或 HAVING 子句,除非它们所在的函数返回的值具有其它数据类型。这样的函数包括 SUBSTRING 和 CAST。

使用 HAVING 子句选择行

HAVING 子句对 GROUP BY 子句设置条件的方式与 WHERE 子句和 SELECT 语句交互的方式类似。WHERE 子句搜索条件在进行分组操作之前应用;而 HAVING 搜索条件在进行分组操作之后应用。HAVING 语法与 WHERE 语法类似,但 HAVING 可以包含聚合函数。HAVING 子句可以引用选择列表中出现的任意项。

下面的查询得到本年度截止到目前的销售额超过 $40,000 的出版商:

USE pubs
SELECT pub_id, total = SUM(ytd_sales)
FROM titles
GROUP BY pub_id
HAVING SUM(ytd_sales) > 40000下面是结果集:

pub_id total        
------ -----------
0877     44219        

(1 row(s) affected)为了确保对每个出版商的计算中至少包含六本书,下面示例使用 HAVING COUNT(*) > 5 消除返回的总数小于六本书的出版商:

USE pubs
SELECT pub_id, total = SUM(ytd_sales)
FROM titles
GROUP BY pub_id
HAVING COUNT(*) > 5下面是结果集:

pub_id total        
------ -----------
0877     44219        
1389     24941        

(2 row(s) affected)理解应用 WHERE、GROUP BY 和 HAVING 子句的正确序列对编写高效的查询代码会有所帮助:

WHERE 子句用来筛选 FROM 子句中指定的操作所产生的行。
GROUP BY 子句用来分组 WHERE 子句的输出。
HAVING 子句用来从分组的结果中筛选行。
对于可以在分组操作之前或之后应用的搜索条件,在 WHERE 子句中指定它们更有效。这样可以减少必须分组的行数。应当在 HAVING 子句中指定的搜索条件只是那些必须在执行分组操作之后应用的搜索条件。

Microsoft® SQL Server™ 2000 查询优化器可处理这些条件中的大多数。如果查询优化器确定 HAVING 搜索条件可以在分组操作之前应用,那么它就会在分组之前应用。查询优化器可能无法识别所有可以在分组操作之前应用的 HAVING 搜索条件。建议将所有这些搜索条件放在 WHERE 子句中而不是 HAVING 子句中。

以下查询显示包含聚合函数的 HAVING 子句。该子句按类型分组 titles 表中的行,并且消除只包含一本书的组:

USE pubs
SELECT type
FROM titles
GROUP BY type
HAVING COUNT(*) > 1下面是结果集:

type
------------------
business
mod_cook
popular_comp
psychology
trad_cook

(5 row(s) affected)以下是没有聚合函数的 HAVING 子句的示例。该子句按类型分组 titles 表中的行,并且消除不是以字母 p 开头的那些类型。

USE pubs
SELECT type
FROM titles
GROUP BY type
HAVING type LIKE 'p%'下面是结果集:

type
------------------
popular_comp
psychology

(2 row(s) affected)如果 HAVING 中包含多个条件,那么这些条件将通过 AND、OR 或 NOT 组合在一起。以下示例显示如何按出版商分组 titles,只包括那些标识号大于 0800、支付的总预付款已超过 $15,000 且销售书籍的平均价格小于 $20 的出版商。

SELECT pub_id, SUM(advance) AS AmountAdvanced,
         AVG(price) AS AveragePrice
FROM pubs.dbo.titles
WHERE pub_id > '0800'
GROUP BY pub_id
HAVING SUM(advance) > $15000
     AND AVG(price) < $20ORDER BY 可以用来为 GROUP BY 子句的输出排序。下面的示例显示使用 ORDER BY 子句以定义返回 GROUP BY 子句中的行的顺序:

SELECT pub_id, SUM(advance) AS AmountAdvanced,
         AVG(price) AS AveragePrice
FROM pubs.dbo.titles
WHERE pub_id > '0800'
    AND price >= $5
GROUP BY pub_id
HAVING SUM(advance) > $15000
     AND AVG(price) < $20
ORDER BY pub_id DESC

猜你喜欢

转载自new-fighter.iteye.com/blog/1497511