Home > Software engineering >  Postgres: column "all_comments.id" must appear in the GROUP BY clause or be used in an agg
Postgres: column "all_comments.id" must appear in the GROUP BY clause or be used in an agg

Time:09-13

I am using with recursive to count the number of all descendants. That would be the following SQL query, but if I specify only root_id for group by, I get I get the following error.

column "all_comments.id" must appear in the GROUP BY clause or be used in an aggregate function

I don't understand why this error occurs with all_comments.id. Also, if I add all_comments.id to group by, this error does not occur, but The original purpose of the all_descendants.id does not count well, and rows of all descendants are returned.

I do not want to remove this column from the SELECT because we need to retrieve the id and other columns as well.

What can I do to resolve this? Your help would be greatly appreciated.

with recursive all_comments as (
   select id, parent_id, id as root_id
   from videos_productvideocomment
   where parent_id is null
   union all
   select c.id, c.parent_id, p.root_id
   from videos_productvideocomment c
     join all_comments p on c.parent_id = p.id
)
select id, root_id, count(*) -1 as all_descendants_count
from all_comments
group by root_id;

Sample data and desired output

                Table "public.videos_productvideocomment"
    Column    |           Type           | Collation | Nullable | Default
-------------- -------------------------- ----------- ---------- ---------
 id           | uuid                     |           | not null |
 created_at   | timestamp with time zone |           | not null |
 updated_at   | timestamp with time zone |           | not null |
 text         | text                     |           | not null |
 commented_at | timestamp with time zone |           | not null |
 edited_at    | timestamp with time zone |           | not null |
 parent_id    | uuid                     |           |          |
 user_id      | uuid                     |           | not null |
 video_id     | uuid                     |           | not null |
[
  {
    id: "uuid1",
    text: "...",
    reply_count: 10,
  },
  {
    id: "uuid5",
    text: "...",
    reply_count: 3,
  },
]

--- Edit --- Using the window function

with recursive all_comments as (
   select id, parent_id, id as root_id
   from videos_productvideocomment
   where parent_id is null
   union all
   select c.id, c.parent_id, p.root_id
   from videos_productvideocomment c
     join all_comments p on c.parent_id = p.id
)
select id, parent_id, root_id, count(root_id) OVER(PARTITION BY root_id) -1 as all_descendants_count
from all_comments

Results obtained

[
  {
    id: "uuid1",
    parent_id: null,
    text: "...",
    reply_count: 10,
  },
  {
    id: "uuid5",
    parent_id: null,
    text: "...",
    reply_count: 3,
  },
  {
    id: "uuid8",
    parent_id: "uuid1",
    text: "...",
    reply_count: 0,
  },
  ...
]

We need to get only those with parent_id null, but if we add WHERE parent_id is null, all_descendants_count will be 0.

CodePudding user response:

Add a filter step after counting over the entire dataset, for example

with recursive all_comments as (
   select id, parent_id, id as root_id
   from videos_productvideocomment
   where parent_id is null
   union all
   select c.id, c.parent_id, p.root_id
   from videos_productvideocomment c
     join all_comments p on c.parent_id = p.id
)
select *
from (
    select id, parent_id, root_id, count(root_id) OVER(PARTITION BY root_id) -1 as all_descendants_count
    from all_comments
) t
where id = root_id
  • Related