为每组数据聚合一组结果
Aggregating a set of results for each set of data
我有一个国家列表。每个短缩写(short)对应1个国家,但是人们喜欢输入除了国家名称之外的其他变量,这会以下面的列表结尾
输出
short nation Students
A A 604
A Austria 6707
A Österreich 3400
AFG Afg 18
AFG Afghanistan 1991
AGL Angola 16
AGN Guinea 2
AL Al 5
AL Albanien 61
ARM Arm 6
ARM Armenien 87
如您所见,计算每个国家/地区的学生总数可以得出 2 或 3 个结果。所以很明显,我想到了这个问题,如果可以计算每个短文本分组的总和,如下所示
预期输出
short nation Students
A A 604
A Austria 6707
A Österreich 3400
A 10711
AFG Afg 18
AFG Afghanistan 1991
AFG 2009
AGL Angola 16
AGN Guinea 2
AL Al 5
AL Albanien 61
AL 66
ARM Arm 6
ARM Armenien 87
ARM 93
我的代码如下
第 1 部分
with natctf as (
SELECT short,
nation,
cast(Studentcount as varchar(6)) as Studentcount
FROM (
SELECT ctf.shorttext as short, ctf.longtext as nation,
count(distinct s.studentid) as Studentcount
from students s
join pupil p on p.id = s.pupilid
join pupilnation pn on pn.pupilid = p.id
join country ctf on ctf.id = pn.coutnryid
Group by ctf.shorttext,ctf.longtext
Order by ctf.shorttext
) t )
第 2 部分
SELECT short, initcap(nation), Studentcount
FROM natctf
UNION ALL
SELECT null as short,
cast(count(nation) as varchar(3)) ||' Nations',
cast(SUM(cast(Studentcount as bigint)) as varchar(10)) ||' Students'
FROM natctf
UNION ALL
一个查询,其中 GROUP BY
短名称和长名称,另一个查询仅按短名称分组。
SELECT x.short,
x.nation,
x.studentcount
FROM (SELECT ctf.shorttext short,
ctf.longtext nation,
count(DISTINCT s.studentid) studentcount
FROM students s
INNER JOIN pupil p
ON p.id = s.pupilid
INNER JOIN pupilnation pn
ON pn.pupilid = p.id
INNER JOIN country ctf
ON ctf.id = pn.coutnryid
GROUP BY ctf.shorttext,
ctf.longtext
UNION ALL
SELECT ctf.shorttext short,
NULL nation,
count(DISTINCT s.studentid) studentcount
FROM students s
INNER JOIN pupil p
ON p.id = s.pupilid
INNER JOIN pupilnation pn
ON pn.pupilid = p.id
INNER JOIN country ctf
ON ctf.id = pn.coutnryid
GROUP BY ctf.shorttext) x
ORDER BY x.short,
x.nation NULLS LAST;
注意,按短名称分组的查询的计数不能是计数的总和,另一个查询returns。那是因为不同的计数。如果一个学生有多个不同的长名和一个短名,他们将被计入每个长名组,但仅在短名组中计一次。
最好的解决方案是使用分组集,这是一个 SQL 标准功能,非常适合您的用例:
SELECT ctf.shorttext as short,
ctf.longtext as nation,
count(...)
FROM country AS ctf JOIN ...
GROUP BY GROUPING SETS ((ctf.shorttext, ctf.longtext), (ctf.shorttext))
ORDER BY ctf.shorttext, ctf.longtext
免责声明:这是PostgreSQL 9.0-9.4版本的解决方案。对于 Postgres 9.5 或更高版本,我会使用 @LaurenzAlbe
的 GROUPING SETS
解决方案
WITH count_nations AS ( -- A
SELECT
*,
sum(students) OVER (PARTITION BY short) as total -- B
FROM nations
)
SELECT short, name, students FROM count_nations -- C
UNION -- E
SELECT short, NULL, total FROM count_nations -- D
ORDER BY
short,
name NULLS LAST, -- F
students
答:WITH
子句使查询更具可读性,因为您不需要两次编写相同的子查询。
B:window 函数 (https://www.postgresql.org/docs/current/static/tutorial-window.html) SUM
对给定帧(这里是 short
列)中的所有值求和。所以你得到的国家总数作为单独的列。
子查询的结果:
short name students total
A A 604 10711
A Austria 6707 10711
A Österreich 3400 10711
AFG Afg 18 2009
AFG Afghanistan 1991 2009
AGL Angola 16 16
AGN Guinea 2 2
AL Al 5 66
AL Albanien 61 66
ARM Arm 6 93
ARM Armenien 87 93
C:选择原始列...
D: 正在选择没有名称的新列...
E: UNION
两种结果。 UNION
使结果不同,因此每个国家只能得到一行。 (UNION ALL
不会做 distinct)
F: 排序结果。对于国家行,NULL
值应该是最后一个。
结果:
short name students
A A 604
A Austria 6707
A Österreich 3400
A 10711
AFG Afg 18
AFG Afghanistan 1991
AFG 2009
AGL Angola 16
AGL 16
AGN Guinea 2
AGN 2
AL Al 5
AL Albanien 61
AL 66
ARM Arm 6
ARM Armenien 87
ARM 93
在您的示例中,您仅为那些有多于一行的国家添加了额外的行。例如,对于 AGN
,您不添加该行。如果这是您的意图,上面链接的 db<>fiddle 会向您显示解决方案:
- 将每个 window 帧的行计数器添加到
WITH
子句中
- 为所有
row_count > 1
的国家过滤 UNION
子查询
我有一个国家列表。每个短缩写(short)对应1个国家,但是人们喜欢输入除了国家名称之外的其他变量,这会以下面的列表结尾
输出
short nation Students
A A 604
A Austria 6707
A Österreich 3400
AFG Afg 18
AFG Afghanistan 1991
AGL Angola 16
AGN Guinea 2
AL Al 5
AL Albanien 61
ARM Arm 6
ARM Armenien 87
如您所见,计算每个国家/地区的学生总数可以得出 2 或 3 个结果。所以很明显,我想到了这个问题,如果可以计算每个短文本分组的总和,如下所示
预期输出
short nation Students
A A 604
A Austria 6707
A Österreich 3400
A 10711
AFG Afg 18
AFG Afghanistan 1991
AFG 2009
AGL Angola 16
AGN Guinea 2
AL Al 5
AL Albanien 61
AL 66
ARM Arm 6
ARM Armenien 87
ARM 93
我的代码如下
第 1 部分
with natctf as (
SELECT short,
nation,
cast(Studentcount as varchar(6)) as Studentcount
FROM (
SELECT ctf.shorttext as short, ctf.longtext as nation,
count(distinct s.studentid) as Studentcount
from students s
join pupil p on p.id = s.pupilid
join pupilnation pn on pn.pupilid = p.id
join country ctf on ctf.id = pn.coutnryid
Group by ctf.shorttext,ctf.longtext
Order by ctf.shorttext
) t )
第 2 部分
SELECT short, initcap(nation), Studentcount
FROM natctf
UNION ALL
SELECT null as short,
cast(count(nation) as varchar(3)) ||' Nations',
cast(SUM(cast(Studentcount as bigint)) as varchar(10)) ||' Students'
FROM natctf
UNION ALL
一个查询,其中 GROUP BY
短名称和长名称,另一个查询仅按短名称分组。
SELECT x.short,
x.nation,
x.studentcount
FROM (SELECT ctf.shorttext short,
ctf.longtext nation,
count(DISTINCT s.studentid) studentcount
FROM students s
INNER JOIN pupil p
ON p.id = s.pupilid
INNER JOIN pupilnation pn
ON pn.pupilid = p.id
INNER JOIN country ctf
ON ctf.id = pn.coutnryid
GROUP BY ctf.shorttext,
ctf.longtext
UNION ALL
SELECT ctf.shorttext short,
NULL nation,
count(DISTINCT s.studentid) studentcount
FROM students s
INNER JOIN pupil p
ON p.id = s.pupilid
INNER JOIN pupilnation pn
ON pn.pupilid = p.id
INNER JOIN country ctf
ON ctf.id = pn.coutnryid
GROUP BY ctf.shorttext) x
ORDER BY x.short,
x.nation NULLS LAST;
注意,按短名称分组的查询的计数不能是计数的总和,另一个查询returns。那是因为不同的计数。如果一个学生有多个不同的长名和一个短名,他们将被计入每个长名组,但仅在短名组中计一次。
最好的解决方案是使用分组集,这是一个 SQL 标准功能,非常适合您的用例:
SELECT ctf.shorttext as short,
ctf.longtext as nation,
count(...)
FROM country AS ctf JOIN ...
GROUP BY GROUPING SETS ((ctf.shorttext, ctf.longtext), (ctf.shorttext))
ORDER BY ctf.shorttext, ctf.longtext
免责声明:这是PostgreSQL 9.0-9.4版本的解决方案。对于 Postgres 9.5 或更高版本,我会使用 @LaurenzAlbe
的GROUPING SETS
解决方案
WITH count_nations AS ( -- A
SELECT
*,
sum(students) OVER (PARTITION BY short) as total -- B
FROM nations
)
SELECT short, name, students FROM count_nations -- C
UNION -- E
SELECT short, NULL, total FROM count_nations -- D
ORDER BY
short,
name NULLS LAST, -- F
students
答:WITH
子句使查询更具可读性,因为您不需要两次编写相同的子查询。
B:window 函数 (https://www.postgresql.org/docs/current/static/tutorial-window.html) SUM
对给定帧(这里是 short
列)中的所有值求和。所以你得到的国家总数作为单独的列。
子查询的结果:
short name students total
A A 604 10711
A Austria 6707 10711
A Österreich 3400 10711
AFG Afg 18 2009
AFG Afghanistan 1991 2009
AGL Angola 16 16
AGN Guinea 2 2
AL Al 5 66
AL Albanien 61 66
ARM Arm 6 93
ARM Armenien 87 93
C:选择原始列...
D: 正在选择没有名称的新列...
E: UNION
两种结果。 UNION
使结果不同,因此每个国家只能得到一行。 (UNION ALL
不会做 distinct)
F: 排序结果。对于国家行,NULL
值应该是最后一个。
结果:
short name students
A A 604
A Austria 6707
A Österreich 3400
A 10711
AFG Afg 18
AFG Afghanistan 1991
AFG 2009
AGL Angola 16
AGL 16
AGN Guinea 2
AGN 2
AL Al 5
AL Albanien 61
AL 66
ARM Arm 6
ARM Armenien 87
ARM 93
在您的示例中,您仅为那些有多于一行的国家添加了额外的行。例如,对于 AGN
,您不添加该行。如果这是您的意图,上面链接的 db<>fiddle 会向您显示解决方案:
- 将每个 window 帧的行计数器添加到
WITH
子句中 - 为所有
row_count > 1
的国家过滤
UNION
子查询