SELECT COUNT(*) - return 0 along with grouped fields if there are no matching rows

白昼怎懂夜的黑 提交于 2019-12-10 13:24:56

问题


I have the following query:

SELECT employee,department,count(*) AS sum FROM items 
WHERE ((employee = 1 AND department = 2) OR 
      (employee = 3 AND department = 4) OR 
      (employee = 5 AND department = 6) OR 
      ([more conditions with the same structure]))
      AND available = true
GROUP BY employee, department;

If there are no items for a pair "employee-department", then the query returns nothing. I'd like it to return zero instead:

 employee | department | sum 
 ---------+------------+--------
 1        |          2 |      0
 3        |          4 |     12  
 5        |          6 |   1234   

EDIT1

Looks like this is not possible, as Matthew PK explains in his answer to a similar question. I was mistakenly assuming Postgres could extract missing values from WHERE clause somehow.

EDIT2

It is possible with some skills. :) Thanks to Erwin Brandstetter!


回答1:


Not possible? Challenge accepted. :)

WITH x(employee, department) AS (
   VALUES
    (1::int, 2::int)
   ,(3, 4)
   ,(5, 6)
    -- ... more combinations
   )
SELECT x.employee, x.department, count(i.employee) AS ct
FROM   x
LEFT   JOIN items i ON i.employee = x.employee
                   AND i.department = x.department
                   AND i.available
GROUP  BY x.employee, x.department;

This will give you exactly what you are asking for. If employee and department aren't integer, cast to the matching type.

Per comment from @ypercube: count() needs to be on a non-null column of items, so we get 0 for non-existing critera, not 1.

Also, pull up additional criteria into the LEFT JOIN condition (i.available in this case), so you don't exclude non-existing criteria.

Performance

Addressing additional question in comment.
This should perform very well. With longer lists of criteria, (LEFT) JOIN is probably the fastest method.

If you need it as fast as possible, be sure to create a multicolumn index like:

CREATE INDEX items_some_name_idx ON items (employee, department);

If (employee, department) should be the PRIMARY KEY or you should have a UNIQUE constraint on the two columns, that would do the trick, too.




回答2:


select employee, department,
    count(
        (employee = 1 and department = 2) or 
        (employee = 3 and department = 4) or 
        (employee = 5 and department = 6) or
        null
    ) as sum
from items
where available = true
group by employee, department;



回答3:


Building on Erwin's join suggestion, this one really works:

with x(employee, department) as (
   values (1, 2)
   )
select
    coalesce(i.employee, x.employee) as employee,
    coalesce(i.department, x.department) as department,
    count(available or null) as ct
from
    x
    full join
    items i on
        i.employee = x.employee
        and
        i.department = x.department
group by 1, 2
order by employee, department


来源:https://stackoverflow.com/questions/13803153/select-count-return-0-along-with-grouped-fields-if-there-are-no-matching-ro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!