我有一个包含电影的简单表:
CREATE TABLE public.films
(
id integer NOT NULL,
title character varying(255),
release_year integer
)
我还有一个查询,按年计算电影数量,所有电影的总数(使用ROLLUP
)和一个窗口函数,可以为每一行添加电影总数:
SELECT
release_year,
COUNT(*),
SUM(COUNT(*) FILTER (WHERE release_year IS NOT NULL)) OVER ()
FROM films
GROUP BY ROLLUP(release_year)
我添加了FILTER (WHERE release_year IS NOT NULL)
部分,因为我想忽略ROLLUP
生成的聚合行。令人惊讶的是,这种过滤并不起作用:
| release_year | count | sum |
----------------------------
| [null] | 225 | 450 |
| 2014 | 57 | 450 | <--- sum should be 225 everywhere
| 2015 | 53 | 450 |
| 2016 | 57 | 450 |
| 2017 | 58 | 450 |
我知道其他一些可能的方法来解决这个问题,例如将窗口函数移动到外部查询或按release_year IS NOT NULL
分区,但我很好奇为什么这个特殊情况不起作用我期望。我错过了什么?
我使用Postgres 10。
答案 0 :(得分:1)
过滤器不能在ROLLUP上工作,因为它会呈现&#39;正在制作ROLLUP。
如果您删除了SUM
,则会看到:
SELECT
-- Adding window function row_number for test:
row_number() OVER() as rn,
release_year,
COUNT(*),
-- Removing SUM and OVER for test:
COUNT(*) FILTER (WHERE release_year IS NOT NULL)
FROM films
GROUP BY ROLLUP(release_year)
输出如下内容:
rn | release_year | count | sum |
----+--------------+-------+------
1 | [null] | 225 | 225 |
2 | 2014 | 57 | 57 |
3 | 2015 | 53 | 53 |
4 | 2016 | 57 | 57 |
5 | 2017 | 58 | 58 |
由于FILTER
适用于ROLLUP,而不适用于&#39; final&#39; table,它没有找到任何NULL release_year,并且由于ROLLUP,它总结了所有release_year&#39;
由于Window Functions&#39;呈现&#39;在#final;&#39;表,当您添加SUM() OVER()
时,它会在列中汇总所有内容;看到列,它全部都是因为它会呈现&#39;呈现&#39;在ROLLUP之后。
编辑(此外):
有一个命令,postgresql计算(我说渲染)一个查询。通常它从WHERE
子句开始,然后分组,然后聚合,并且最后它总是在窗口函数中。
如果您运行EXPLAIN
,您可以更清楚地看到它:
QUERY PLAN
------------------------------------------------------------------------------
WindowAgg (cost=XXX.XX..XXX.XX rows=XXX width=XX)
-> GroupAggregate (cost=XXX.XX..XXX.XX rows=XXX width=XX)
Group Key: release_year
Group Key: ()
-> Sort (cost=XXX.XX..XXX.XX rows=XXXX width=X)
Sort Key: release_year
-> Seq Scan on films (cost=X.X..XX.XX rows=XXXX width=X)
首先,它会执行WHERE
子句,但由于查询中没有WHERE
,因此它会从表格中获取所有行(在电影上扫描)。
第二,它按发布日期执行组和订单,因为它位于GROUP BY ROLLUP
子句中(排序)。
第三,它对分组数据(GroupAggregate)运行聚合函数COUNT
。由于FILTER
只是CASE WHEN
的替代,因此它会在分组数据上运行。有关FILTER here
最后,它在聚合(WindowAgg)上运行窗口函数。
一些注释:
当我说&#39; over&#39;时,我的意思是内部postgresql有一个时间表 - 这个&#39; final&#39;表我在上面提到的,实际上是一组数据 - 它运行你在查询中使用的所有函数或子句。
运行窗口函数后,它返回一个RESULT表,这就是你得到的。但在中间,postgresql使用数据集来获得你所要求的内容。
<强>结论强>
从这个意义上讲,您的查询不会返回您想要的内容,因为FILTER对分组数据起作用,而不是在release_year上有NULL的数据集。只有窗口函数才能访问此NULL,因为在返回给您之前对最后一组数据进行处理,并且由于窗口函数的用途有限,您必须查询此RESULT表以获得您想要的内容。
然而,如果你想了解更多:
更多在postgresql手册中: