我正在做“Murach's MySQL 第三版”的练习,它给出了以下提示:
“11. 编写一条 SELECT 语句,使用聚合窗口函数来计算发票总额总和的移动平均值。返回这些列:
结果集应按发票月份和框架进行分组 移动平均线应包括当前行加上之前的三行 当前行。”
所引用的发票表如下所示(总共 114 行):
发票_id | 供应商_id | 发票号码 | 发票日期 | 发票_总计 | 付款_总计 | 信用总额 | terms_id | 发票到期日期 | 付款日期 |
---|---|---|---|---|---|---|---|---|---|
1 | 122 | 989319-457 | 2018-04-08 | 3813.33 | 3813.33 | 0 | 3 | 2018-05-08 | 2018-05-07 |
我最初想出的解决方案是这样的:
SELECT
EXTRACT(MONTH FROM invoice_date) AS months,
SUM(invoice_total) AS invoice_total_sum,
AVG(invoice_total) OVER(
ORDER BY EXTRACT(MONTH FROM invoice_date)
ROWS 3 PRECEDING
) AS rolling_avg
FROM
invoices
GROUP BY
months;
当我运行此程序时,出现以下错误:
Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'ap.invoices.invoice_total' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
查看作者提供的解决方案并重新阅读问题后,我意识到我应该取每月金额的平均值,而不仅仅是发票本身的平均值。所以真正的解决方案是这样的:
SELECT
EXTRACT(MONTH FROM invoice_date) AS months,
SUM(invoice_total) AS invoice_total_sum,
AVG(SUM(invoice_total)) OVER( # why does this need the SUM() function?
ORDER BY EXTRACT(MONTH FROM invoice_date)
ROWS 3 PRECEDING
) AS rolling_avg
FROM
invoices
GROUP BY
months;
这会产生一个像这样的输出表(有 5 行):
月 | 发票总金额 | 滚动平均 |
---|---|---|
4 | 5828.18 | 5828.180000 |
但是,我不明白的是为什么第一个解决方案无法运行。我知道为什么这不是练习所寻找的内容,但我不明白是什么导致它出错。
我的理解是,像
sum()
和 avg()
这样的聚合函数给出有关“函数依赖”的错误的原因是,否则你会得到不一致数量的输出行。如果我尝试仅使用 sum(invoice_total)
和 invoice_date
,它将生成一行用于求和,并生成许多行用于日期,并且无法将它们解析为输出表(不更改 only_full_group_by 模式)。但平均函数也是一个聚合函数,那么为什么它不按照 group by
子句的指定,对每个月的所有发票总额进行平均呢?
(1)
AVG
是一个聚合函数,但这里它与 OVER
子句一起使用,使其成为窗口函数。使用窗口函数,结果显示在每一行上。而使用常规聚合函数,它会组合多行并将结果显示在一行上。
在您的查询中,您可以像下面一样删除
SUM
,并且可以看到 AVG
的分组仅适用于当前行和前 3 行;结果显示在每一行
SELECT
EXTRACT(MONTH FROM invoice_date) AS months,
invoice_date,
AVG(invoice_total) OVER(
ORDER BY EXTRACT(MONTH FROM invoice_date)
ROWS 3 PRECEDING
) AS rolling_avg
FROM
invoices
ORDER BY EXTRACT(MONTH FROM invoice_date)
(2) 你的下一个问题是为什么
SUM(invoice_total)
位于 AVG
内部。
这里,AVG 函数正在对已按 EXTRACT(MONTH FROM Invoice_date) 分组的行进行求和。在窗口函数内部,分组的行按 ORDER BY 排序,以便当前行回顾过去 3 个 SUM 行(过去 3 个月)以获得 AVG
希望这有帮助