如何计算包括子类别的相关行?
How to count related rows including subcategories?
我在 Postgres 12.3 数据库中有一些 table。
第一个名字是category
:
id|template_id|name |entry_count|is_base_template|can_rename|can_delete|section|userId|parentCategoryId|
--|-----------|---------------|-----------|----------------|----------|----------|-------|------|----------------|
1| |Notes | 0|true |true |true |A | | |
2| |ToDo | 0|true |true |true |A | | |
3| 1|Notes | 0|false |true |true |A | 1| |
4| 2|ToDo | 0|false |true |true |A | 1| |
5| |Must Do | 0|false | | |A | | 4|
6| |Important notes| 0|false | | |A | | 3|
第二个 table 称为 entry
- 这与手头的问题无关。
还有 link table category_entries_entry
:
categoryId|entryId|
----------|-------|
4| 1|
5| 5|
5| 6|
4| 7|
3| 8|
6| 9|
一个类别可以拥有children,如果parentCategoryId
不为NULL那么我们正在处理一个child。例如,id = 5
的类别是 id = 4
的子类别。 Children不能有自己的children,所以只能嵌套一层。
我需要计算每个类别的条目数包括 个子类别。
这个请求基本上满足了我的需要。但是没有考虑到用户:
SELECT COALESCE(c."parentCategoryId" , c.id) as cat_id , COUNT(*) as entries_in_cat
FROM category c JOIN
category_entries_entry r
ON c.id = r."categoryId"
WHERE c.is_base_template = false
GROUP BY cat_id;
Returns:
cat_id|entries_in_cat|
------|--------------|
4| 4|
3| 2|
category
table 也有 userId
并且应该只对给定用户执行计数。值得注意的是,只有根类别具有 userId
.
的条目
而且我想列出子类别及其数量另外。因此,给定样本的 期望输出 和 userId = 1
是:
cat_id|entries_in_cat|
------|--------------|
5| 2|
4| 4|
6| 1|
3| 2|
这是一个细分:
1) 类别编号 6 是第 3 个类别的子类别,它有 1 个条目,所以结果是正确的。
2) 类别编号3是一个类别(也就是说,它没有parent),它包含1个条目,另一个应该来自第6个子类别,总共2个。您的脚本 returns 1 是错误的。
3) 类别编号 5 是第四类别的子类别,它包含 2 个条目。你的脚本 returns 也是 2 是正确的。
4) 类别编号 4 是一个类别,它有 2 个自己的条目,另外两个来自第 5 个子类别,总共 4 个。您的脚本 returns 2 是错误的。应该 return 4.
我怎样才能做到这一点?
由于过滤仅适用于父类别,您需要先在父类别中应用它:然后您可以获得子类别。拥有父类别和子类别后,您可以将它们加入条目并计算它们。
总之,查询应如下所示:
with
c as (
SELECT id
FROM category
WHERE userId = 1 AND is_base_template = false
),
s as (
SELECT d.id
FROM c
JOIN category d on d.parentCategoryId = c.id
)
SELECT u.id, count(*) as entries_in_cat
FROM (select id from c union select id from s) u
JOIN category_entries_entry r ON u.id = r."categoryId"
GROUP BY u.id
这完成了 单层 嵌套的工作:
要仅列出根类别,计数包括子类别:
WITH root AS (
SELECT id AS cat_id, id AS sub_id
FROM category
WHERE is_base_template = false
AND "userId" = 1
)
SELECT c.cat_id, count(*)::int AS entries_in_cat
FROM (
TABLE root
UNION ALL
SELECT r.cat_id, c.id
FROM root r
JOIN category c ON c."parentCategoryId" = r.cat_id
) c
JOIN category_entries_entry e ON e."categoryId" = c.sub_id
GROUP BY c.cat_id;
重点是加入 sub_id
,但按 cat_id
分组。
要像上面那样列出根类别和子类别另外:
WITH root AS (
SELECT id AS cat_id, id AS sub_id
FROM category
WHERE is_base_template = false
AND "userId" = 1
)
, ct AS (
SELECT c.cat_id, c.sub_id, count(*)::int AS ct
FROM (
TABLE root
UNION ALL
SELECT r.cat_id, c.id AS sub_id
FROM root r
JOIN category c ON c."parentCategoryId" = r.cat_id
) c
JOIN category_entries_entry e ON e."categoryId" = c.sub_id
GROUP BY c.cat_id, c.sub_id
)
SELECT cat_id, sum(ct)::int AS entries_in_cat
FROM ct
GROUP BY 1
UNION ALL
SELECT sub_id, ct
FROM ct
WHERE cat_id <> sub_id;
db<>fiddle here
对于任意数量的嵌套级别,使用递归 CTE。示例:
关于可选的短语法 TABLE parent
:
我在 Postgres 12.3 数据库中有一些 table。
第一个名字是category
:
id|template_id|name |entry_count|is_base_template|can_rename|can_delete|section|userId|parentCategoryId|
--|-----------|---------------|-----------|----------------|----------|----------|-------|------|----------------|
1| |Notes | 0|true |true |true |A | | |
2| |ToDo | 0|true |true |true |A | | |
3| 1|Notes | 0|false |true |true |A | 1| |
4| 2|ToDo | 0|false |true |true |A | 1| |
5| |Must Do | 0|false | | |A | | 4|
6| |Important notes| 0|false | | |A | | 3|
第二个 table 称为 entry
- 这与手头的问题无关。
还有 link table category_entries_entry
:
categoryId|entryId|
----------|-------|
4| 1|
5| 5|
5| 6|
4| 7|
3| 8|
6| 9|
一个类别可以拥有children,如果parentCategoryId
不为NULL那么我们正在处理一个child。例如,id = 5
的类别是 id = 4
的子类别。 Children不能有自己的children,所以只能嵌套一层。
我需要计算每个类别的条目数包括 个子类别。
这个请求基本上满足了我的需要。但是没有考虑到用户:
SELECT COALESCE(c."parentCategoryId" , c.id) as cat_id , COUNT(*) as entries_in_cat
FROM category c JOIN
category_entries_entry r
ON c.id = r."categoryId"
WHERE c.is_base_template = false
GROUP BY cat_id;
Returns:
cat_id|entries_in_cat|
------|--------------|
4| 4|
3| 2|
category
table 也有 userId
并且应该只对给定用户执行计数。值得注意的是,只有根类别具有 userId
.
而且我想列出子类别及其数量另外。因此,给定样本的 期望输出 和 userId = 1
是:
cat_id|entries_in_cat|
------|--------------|
5| 2|
4| 4|
6| 1|
3| 2|
这是一个细分:
1) 类别编号 6 是第 3 个类别的子类别,它有 1 个条目,所以结果是正确的。
2) 类别编号3是一个类别(也就是说,它没有parent),它包含1个条目,另一个应该来自第6个子类别,总共2个。您的脚本 returns 1 是错误的。
3) 类别编号 5 是第四类别的子类别,它包含 2 个条目。你的脚本 returns 也是 2 是正确的。
4) 类别编号 4 是一个类别,它有 2 个自己的条目,另外两个来自第 5 个子类别,总共 4 个。您的脚本 returns 2 是错误的。应该 return 4.
我怎样才能做到这一点?
由于过滤仅适用于父类别,您需要先在父类别中应用它:然后您可以获得子类别。拥有父类别和子类别后,您可以将它们加入条目并计算它们。
总之,查询应如下所示:
with
c as (
SELECT id
FROM category
WHERE userId = 1 AND is_base_template = false
),
s as (
SELECT d.id
FROM c
JOIN category d on d.parentCategoryId = c.id
)
SELECT u.id, count(*) as entries_in_cat
FROM (select id from c union select id from s) u
JOIN category_entries_entry r ON u.id = r."categoryId"
GROUP BY u.id
这完成了 单层 嵌套的工作:
要仅列出根类别,计数包括子类别:
WITH root AS (
SELECT id AS cat_id, id AS sub_id
FROM category
WHERE is_base_template = false
AND "userId" = 1
)
SELECT c.cat_id, count(*)::int AS entries_in_cat
FROM (
TABLE root
UNION ALL
SELECT r.cat_id, c.id
FROM root r
JOIN category c ON c."parentCategoryId" = r.cat_id
) c
JOIN category_entries_entry e ON e."categoryId" = c.sub_id
GROUP BY c.cat_id;
重点是加入 sub_id
,但按 cat_id
分组。
要像上面那样列出根类别和子类别另外:
WITH root AS (
SELECT id AS cat_id, id AS sub_id
FROM category
WHERE is_base_template = false
AND "userId" = 1
)
, ct AS (
SELECT c.cat_id, c.sub_id, count(*)::int AS ct
FROM (
TABLE root
UNION ALL
SELECT r.cat_id, c.id AS sub_id
FROM root r
JOIN category c ON c."parentCategoryId" = r.cat_id
) c
JOIN category_entries_entry e ON e."categoryId" = c.sub_id
GROUP BY c.cat_id, c.sub_id
)
SELECT cat_id, sum(ct)::int AS entries_in_cat
FROM ct
GROUP BY 1
UNION ALL
SELECT sub_id, ct
FROM ct
WHERE cat_id <> sub_id;
db<>fiddle here
对于任意数量的嵌套级别,使用递归 CTE。示例:
关于可选的短语法 TABLE parent
: