如何计算包含子类别的相关行?

inn6fuwd  于 2021-08-09  发布在  Java
关注(0)|答案(2)|浏览(442)

我在postgres12.3数据库中有一些表。
第一个被命名为 category :

  1. id|template_id|name |entry_count|is_base_template|can_rename|can_delete|section|userId|parentCategoryId|
  2. --|-----------|---------------|-----------|----------------|----------|----------|-------|------|----------------|
  3. 1| |Notes | 0|true |true |true |A | | |
  4. 2| |ToDo | 0|true |true |true |A | | |
  5. 3| 1|Notes | 0|false |true |true |A | 1| |
  6. 4| 2|ToDo | 0|false |true |true |A | 1| |
  7. 5| |Must Do | 0|false | | |A | | 4|
  8. 6| |Important notes| 0|false | | |A | | 3|

第二个表称为 entry -与眼前的问题无关。
还有一个链接表 category_entries_entry :

  1. categoryId|entryId|
  2. ----------|-------|
  3. 4| 1|
  4. 5| 5|
  5. 5| 6|
  6. 4| 7|
  7. 3| 8|
  8. 6| 9|

一个类别可以拥有孩子,如果 parentCategoryId 如果不是空的,那么我们要处理的是一个孩子。例如,具有 id = 5 是的子类别 id = 4 . 孩子不能有自己的孩子,所以只能有一个层次的筑巢。
我需要计算每个类别的条目数,包括子类别。
这个请求基本上满足了我的需要。但不考虑用户:

  1. SELECT COALESCE(c."parentCategoryId" , c.id) as cat_id , COUNT(*) as entries_in_cat
  2. FROM category c JOIN
  3. category_entries_entry r
  4. ON c.id = r."categoryId"
  5. WHERE c.is_base_template = false
  6. GROUP BY cat_id;

退货:

  1. cat_id|entries_in_cat|
  2. ------|--------------|
  3. 4| 4|
  4. 3| 2|

这个 category 表也有 userId 并且只应对给定的用户执行计数。值得注意的是,只有根类别才有 userId .
我想列出子类别和它们的计数。所以期望的输出 userId = 1 对于给定的样本:

  1. cat_id|entries_in_cat|
  2. ------|--------------|
  3. 5| 2|
  4. 4| 4|
  5. 6| 1|
  6. 3| 2|

下面是一个分解:
1) 类别6是第三个类别的子类别,它有一个条目,因此结果是正确的。
2) 类别3是一个类别(也就是说,它没有父类别),它包含1个条目,另一个条目应该来自第6个子类别,即总共2个。脚本返回错误的1。
3) 类别编号5是第4个类别的子类别,它包含2个条目。您的脚本还返回2,这是正确的。
4) 类别编号4是一个类别,它有两个自己的条目,另外两个来自第5个子类别,总共4个。脚本返回2,这是错误的。它应该返回4。
我怎样才能做到这一点?

thtygnil

thtygnil1#

这就完成了单级嵌套的工作:
要仅列出根类别,计数包括子类别:

  1. WITH root AS (
  2. SELECT id AS cat_id, id AS sub_id
  3. FROM category
  4. WHERE is_base_template = false
  5. AND "userId" = 1
  6. )
  7. SELECT c.cat_id, count(*)::int AS entries_in_cat
  8. FROM (
  9. TABLE root
  10. UNION ALL
  11. SELECT r.cat_id, c.id
  12. FROM root r
  13. JOIN category c ON c."parentCategoryId" = r.cat_id
  14. ) c
  15. JOIN category_entries_entry e ON e."categoryId" = c.sub_id
  16. GROUP BY c.cat_id;

关键是加入 sub_id ,但分组依据 cat_id .
要列出如上所述的根类别以及其他子类别,请执行以下操作:

  1. WITH root AS (
  2. SELECT id AS cat_id, id AS sub_id
  3. FROM category
  4. WHERE is_base_template = false
  5. AND "userId" = 1
  6. )
  7. , ct AS (
  8. SELECT c.cat_id, c.sub_id, count(*)::int AS ct
  9. FROM (
  10. TABLE root
  11. UNION ALL
  12. SELECT r.cat_id, c.id AS sub_id
  13. FROM root r
  14. JOIN category c ON c."parentCategoryId" = r.cat_id
  15. ) c
  16. JOIN category_entries_entry e ON e."categoryId" = c.sub_id
  17. GROUP BY c.cat_id, c.sub_id
  18. )
  19. SELECT cat_id, sum(ct)::int AS entries_in_cat
  20. FROM ct
  21. GROUP BY 1
  22. UNION ALL
  23. SELECT sub_id, ct
  24. FROM ct
  25. WHERE cat_id <> sub_id;

db<>在这里摆弄
对于任意数量的嵌套级别,请使用递归cte。例子:
如何在多对多关系中链接行,其中类型有父母、祖父母等
关于可选短语法 TABLE parent :
是否有选择*的快捷方式?

展开查看全部
ss2ws0br

ss2ws0br2#

因为过滤只应用于父类别,所以您需要首先将其应用于父类别:然后才能获得子类别。一旦有了父类别和子类别,就可以将它们加入到条目中并对它们进行计数。
总之,查询应该如下所示:

  1. with
  2. c as (
  3. SELECT id
  4. FROM category
  5. WHERE userId = 1 AND is_base_template = false
  6. ),
  7. s as (
  8. SELECT d.id
  9. FROM c
  10. JOIN category d on d.parentCategoryId = c.id
  11. )
  12. SELECT u.id, count(*) as entries_in_cat
  13. FROM (select id from c union select id from s) u
  14. JOIN category_entries_entry r ON u.id = r."categoryId"
  15. GROUP BY u.id
展开查看全部

相关问题