(SQL 初学者问题)帮助理解 HAVING 和 GROUP BY 是如何应用的

Posted

技术标签:

【中文标题】(SQL 初学者问题)帮助理解 HAVING 和 GROUP BY 是如何应用的【英文标题】:(SQL Beginner Question) help understanding how HAVING and GROUP BY are applied 【发布时间】:2021-05-21 11:47:55 【问题描述】:

我正在处理 SQL Server 中的 Northwinds 练习数据库的练习问题,但我无法理解此解决方案的工作原理。任何解释它的帮助将不胜感激(欢迎对我的代码效率或可读性提出任何批评 - 我是初学者)。

练习题有两个问题。

第一个是:定义在 2016 年至少下过 1 笔价值等于或大于 10,000 美元的订单的客户。

为此我写道:

SELECT
    o.CustomerID,
    c.CompanyName,
    o.OrderID,
    SUM(d.UnitPrice*d.Quantity) AS 'TotalOrderAmount'
FROM 
    Orders AS o
JOIN
    Customers AS c ON o.CustomerID = c.CustomerID
JOIN
    OrderDetails AS d ON d.OrderID = o.OrderID
WHERE
    YEAR(o.OrderDate) = 2016
GROUP BY
    o.CustomerID, c.CompanyName, o.OrderID
HAVING
    SUM(d.UnitPrice * d.Quantity) >= 10000
ORDER BY
    TotalOrderAmount DESC 

第二个问题是:定义 2016 年订单总额为 15,000 美元或以上的客户。

在书后的帮助下我写的解决方案是:

SELECT
    c.CustomerID,
    c.CompanyName,
    SUM(d.UnitPrice*d.Quantity) AS 'TotalOrderAmount'
FROM 
    Orders AS o
JOIN
    Customers AS c ON o.CustomerID = c.CustomerID
JOIN
    OrderDetails AS d ON d.OrderID = o.OrderID
WHERE
    YEAR(o.OrderDate) = 2016
GROUP BY
    c.CustomerID, c.CompanyName
HAVING
    SUM(d.UnitPrice * d.Quantity) >= 15000
ORDER BY
    TotalOrderAmount DESC 

我只是不明白如何将单个订单的 SUM 报表转换为询问客户的年度总额是多少。这本书特别提到了更改 GROUP BY 级别,所以我只是这样做了,但我仍然不确定它是如何解决的。

提前致谢。

【问题讨论】:

我真的不明白你在这里不明白什么。具体来说,您在这里不清楚的是什么? 仔细查看 GROUP BY 子句。有什么区别?一个有 OrderID,一个没有。那么这对 SUM 计算的值有什么不同呢? 第一个查询不完全正确,如果有多个这样的订单,它会给相同的客户多次。顺便说一句:YEAR(...) = 效率低下,通常使用date >= 'yyyy-01-01' AND date < 'nxtyear-01-01' 会更快 @Larnu 第二个问题想要的年度合并订单总额大于或等于 15,000 美元,但在 HAVING 中,我认为它只是在寻找超过 15,000 美元的订单,而不是年终总销售额 15,000 美元的订单。 @Larnu 也许我误解了这个问题。当答案部分说“这里只需要注释掉 Select 子句和 Group By 子句中对 OrderID 的引用时,我有点被甩了。这样做,我们在客户级别进行分组,而不是在订单级别。” 【参考方案1】:

试试这个:删除HAVING 子句,并将结果过滤到单个客户和公司,这样您就可以更仔细地查看它们:

SELECT
    o.CustomerID,
    c.CompanyName,
    o.OrderID,
    SUM(d.UnitPrice*d.Quantity) AS TotalOrderAmount
FROM 
    Orders AS o
JOIN
    Customers AS c ON o.CustomerID = c.CustomerID
JOIN
    OrderDetails AS d ON d.OrderID = o.OrderID
WHERE
    YEAR(o.OrderDate) = 2016
    -- pick a customer and company that gives you some orders
    AND c.CustomerID = 12345
    AND c.CompanyName = 'Acme Corp'
GROUP BY
    o.CustomerID, c.CompanyName, o.OrderID

您应该会为每个 OrderID 看到不同的 TotalOrderAmount

现在从SELECT 子句和GROUP BY 子句中删除o.OrderID

SELECT
    o.CustomerID,
    c.CompanyName,
    SUM(d.UnitPrice*d.Quantity) AS TotalOrderAmount
FROM 
    Orders AS o
JOIN
    Customers AS c ON o.CustomerID = c.CustomerID
JOIN
    OrderDetails AS d ON d.OrderID = o.OrderID
WHERE
    YEAR(o.OrderDate) = 2016
    -- use the same filters as before
    AND c.CustomerID = 12345
    AND c.CompanyName = 'Acme Corp'
GROUP BY
    o.CustomerID, c.CompanyName

您现在应该只看到一行,它的 TotalOrderAmount 将是所有之前的 TotalOrderAmount 值加在一起。这是因为以前因为具有不同 OrderID 值而分开的行现在被合并到同一个组中。当我们计算那个新的更大组的SUM 时,它包含了之前那些组的所有值。

HAVING 子句只查看与TotalOrderAmount 列相同的等式,并使用它从结果中过滤整个组

【讨论】:

以上是关于(SQL 初学者问题)帮助理解 HAVING 和 GROUP BY 是如何应用的的主要内容,如果未能解决你的问题,请参考以下文章

读SQL进阶教程笔记09_HAVING上

数据库中having 和where有啥区别

sql中的group by 和 having 用法解析

mysql having的用法

SQL分组查询GroupBy加having

简单查询语句