使用 LEFT JOIN 的查询不返回计数为 0 的行

问题描述 投票:0回答:2

我试图让以下内容返回 PostgreSQL 中使用左连接的每个组织的计数,但我无法弄清楚为什么它不起作用:

  select o.name as organisation_name,
         coalesce(COUNT(exam_items.id)) as total_used
  from organisations o
  left join exam_items e on o.id = e.organisation_id
  where e.item_template_id = #{sanitize(item_template_id)}
  and e.used = true
  group by o.name
  order by o.name

使用

coalesce
似乎不起作用。我已经无计可施了!任何帮助都将不胜感激!

为了澄清什么不起作用,目前查询仅返回计数大于 0 的组织的值。我希望它为 every 组织返回一行,无论计数如何。

表格定义:

TABLE exam_items
  id serial NOT NULL
  exam_id integer
  item_version_id integer
  used boolean DEFAULT false
  question_identifier character varying(255)
  organisation_id integer
  created_at timestamp without time zone NOT NULL
  updated_at timestamp without time zone NOT NULL
  item_template_id integer
  stem_id integer
  CONSTRAINT exam_items_pkey PRIMARY KEY (id)

TABLE organisations
  id serial NOT NULL
  slug character varying(255)
  name character varying(255)
  code character varying(255)
  address text
  organisation_type integer
  created_at timestamp without time zone NOT NULL
  updated_at timestamp without time zone NOT NULL
  super boolean DEFAULT false
  CONSTRAINT organisations_pkey PRIMARY KEY (id)
sql postgresql count left-join
2个回答
86
投票

修复
LEFT JOIN

这应该有效:

SELECT o.name AS organisation_name, count(e.id) AS total_used
FROM   organisations   o
LEFT   JOIN exam_items e ON e.organisation_id = o.id 
                        AND e.item_template_id = #{sanitize(item_template_id)}
                        AND e.used
GROUP  BY o.name
ORDER  BY o.name;

您有一个

LEFT [OUTER] JOIN
,但后来的
WHERE
条件使其表现得像一个普通的
[INNER] JOIN

将条件移至
JOIN
子句以使其按预期工作。这样,只有满足所有这些条件的行才会首先连接(或者 right 表中的列会填充 NULL)。就像您所拥有的那样,连接的行实际上会在LEFT JOIN之后
测试附加条件,如果不通过则将其删除,就像普通的
JOIN
一样。

count()

 从一开始就不会返回 NULL。在这方面,这是聚合函数中的一个例外。因此,即使有额外的参数,
COALESCE(COUNT(col))
never也是有意义的。 说明书:

需要注意的是,

除了count

之外,这些函数在没有选择行时返回空值。

我的粗体强调。参见:

  • 计算一行中 NULL 属性的数量

count()

 必须位于定义为 
NOT NULL
 的列上(如 
e.id
),或者示例中联接条件保证 
NOT NULL
e.organisation_id
e.item_template_id
e.used
)的位置。

由于

used

 是类型 
boolean
,因此表达式 
e.used = true
 是燃烧至仅 
e.used
 的噪音。

由于

o.name

 未定义 
UNIQUE NOT NULL
,您可能需要改为 
GROUP BY o.id
id
 是 PK) - 除非您
打算折叠具有相同名称(包括 NULL)的行。

先聚合,后加入

如果在此过程中计算了

exam_items

 的大部分或全部行,则此等效查询通常会更快/更便宜:

SELECT o.id, o.name AS organisation_name, e.total_used FROM organisations o LEFT JOIN ( SELECT organisation_id AS id -- alias to simplify join syntax , count(*) AS total_used -- count(*) = fastest to count all FROM exam_items WHERE item_template_id = #{sanitize(item_template_id)} AND used GROUP BY 1 ) e USING (id) ORDER BY o.name, o.id;
(这是假设您不想像上面提到的那样折叠具有相同名称的行 - 典型情况。)

现在我们可以在子查询中使用更快/更简单的

count(*)

,并且外部
GROUP BY
中不需要
SELECT

参见:

  • 单个查询中多次调用 array_agg()

15
投票
为了说清楚,

重要的一行是

GROUP BY MAIN_TABLE

,它将处理来自 
SOME_TABLE
 的 NULL 值

SELECT COUNT(ST.ID) FROM MAIN_TABLE MT LEFT JOIN SOME_TABLE ST ON MT.ID = ST.MT_ID GROUP BY MT.ID -- this line is a must
    
© www.soinside.com 2019 - 2024. All rights reserved.