SELECT COUNT(*) - 如果没有匹配的行,则返回0以及分组的字段

时间:2012-12-10 14:41:44

标签: sql postgresql count left-join aggregate-functions

我有以下查询:

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;

如果“employee-department”对没有项目,则查询不返回任何内容。我希望它返回零代替:

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

EDIT1

看起来这是不可能的,正如马修PK解释in his answer to a similar question。我错误地认为Postgres可以以某种方式从WHERE子句中提取缺失值。

EDIT2

有一些技能是可能的。 :)感谢Erwin Brandstetter!

3 个答案:

答案 0 :(得分:6)

不可能吗?接受挑战。 :)

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;

这将为您提供完全您要求的内容。如果employeedepartment不是整数,则转换为匹配类型。

来自@ypercube的评论:count()需要位于items的非空列上,因此我们为不存在的critera获取0,而不是1。< / p>

另外,将其他条件提升到LEFT JOIN条件(本例中为i.available),因此您不排除不存在的条件。

性能

在评论中解决其他问题 这应该表现得很好。使用较长的标准列表,(LEFT) JOIN可能是最快的方法。

如果您需要尽快,请务必创建multicolumn index之类的:

CREATE INDEX items_some_name_idx ON items (employee, department);

如果(employee, department)应该是PRIMARY KEY,或者您应该对这两列有UNIQUE约束,那也可以解决这个问题。

答案 1 :(得分: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;

答案 2 :(得分:2)

基于Erwin的加入建议,这个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