对 Group By 后的结果进行过滤(HAVING 等效)
SQL 中的 HAVING
在 SQL 中,可以使用 HAVING 对 GROUP BY 后的聚合结果进行进一步过滤:
SELECT ID, Name, AVG(Price) FROM
(
SELECT s.ID, s.Name, p.Name AS ProductName, p.Price
FROM Student s
INNER JOIN Product AS p
ON s.ID = p.Sid
) AS t
GROUP BY ID, Name
HAVING ID = 2Code language: PHP (php)
LINQ 中的对应写法
在 LINQ 中,没有 HAVING 关键字。不管是过滤 SELECT 的原始行,还是过滤 GROUP BY 后的分组结果,统一使用 where。
using (var db = new DataClasses1DataContext())
{
var query = from s in db.Student
join p in db.Product
on s.ID equals p.Sid
group new { s, p } by new { s.ID, s.Name } into g
where g.Key.ID == 1
select new
{
g.Key.ID,
g.Key.Name,
sum = g.Sum(m => m.p.Price)
};
var result = query.ToList();
}Code language: JavaScript (javascript)
执行逻辑解析
| 步骤 | 代码 | 说明 |
|---|---|---|
| ① 连接 | join p in db.Product on s.ID equals p.Sid | 学生表与产品表内连接 |
| ② 分组 | group new { s, p } by new { s.ID, s.Name } into g | 按学生 ID 和姓名分组 |
| ③ 过滤 | where g.Key.ID == 1 | 对分组结果进行过滤(等效 SQL 的 HAVING) |
| ④ 投影 | select new { g.Key.ID, g.Key.Name, sum = g.Sum(m => m.p.Price) } | 取分组键字段,并对组内价格求和 |
SQL 生成对照
上述 LINQ 最终生成的 SQL 等价于:
SELECT s.ID, s.Name, SUM(p.Price) AS sum
FROM Student s
INNER JOIN Product p ON s.ID = p.Sid
GROUP BY s.ID, s.Name
HAVING s.ID = 1Code language: PHP (php)
关键要点
where的位置决定语义: 写在group by之前的where等效于 SQL 的WHERE(分组前过滤行);写在group by ... into g之后的where等效于 SQL 的HAVING(分组后过滤组)- 分组后过滤的写法:
where g.Key.ID == 1中的g.Key对应 SQL 中GROUP BY的键列 - 聚合后过滤: 如果要根据聚合函数结果过滤(如
SUM > 1000),写法为where g.Sum(m => m.p.Price) > 1000
分组前过滤 vs 分组后过滤对比
// 分组前过滤(等效 WHERE)—— 先过滤行,再分组
var query1 = from s in db.Student
join p in db.Product on s.ID equals p.Sid
where s.ID == 1
group new { s, p } by new { s.ID, s.Name } into g
select new { g.Key.ID, g.Key.Name, sum = g.Sum(m => m.p.Price) };
// 分组后过滤(等效 HAVING)—— 先分组,再过滤组
var query2 = from s in db.Student
join p in db.Product on s.ID equals p.Sid
group new { s, p } by new { s.ID, s.Name } into g
where g.Key.ID == 1
select new { g.Key.ID, g.Key.Name, sum = g.Sum(m => m.p.Price) };Code language: JavaScript (javascript)
两种写法生成的 SQL 不同:前者将条件置于
GROUP BY之前,后者置于HAVING子句中。在索引利用和性能上可能有所差异,需根据实际数据分布选择。
Previous: linq中的子查询
Next: 排序查找orderby