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

njthzxwz  于 2023-11-18  发布在  PostgreSQL
关注(0)|答案(2)|浏览(183)

我试图让下面的代码返回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的组织的值。我希望它为每个组织返回一行,而不管计数如何。
表格定义:

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)

mccptt67

mccptt671#

修复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 是有意义的,即使有额外的参数。The manual:
需要注意的是,除了count,当没有选择行时,这些函数返回空值。
粗体强调我的。参见:

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

count()必须位于定义为NOT NULL的列上(如e.id),或者连接条件保证NOT NULLe.organisation_ide.item_template_ide.used)。
由于used的类型是boolean,表达式e.used = true是噪声,只剩下e.used
由于o.name没有定义为UNIQUE NOT NULL,所以您可能希望改为GROUP BY o.idid是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;


(This假设您不想像上面提到的那样折叠同名的行-这是典型的情况。)
现在我们可以在子查询中使用更快/更简单的count(*),并且在外部SELECT中不需要GROUP BY
参见:

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

6fe3ivhb2#

为了说明,
重要行是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

字符串

相关问题