【问题标题】:Query with LEFT JOIN not returning rows for count of 0使用 LEFT JOIN 查询不返回计数为 0 的行
【发布时间】:2013-03-06 05:54:50
【问题描述】:

我正在尝试获取以下内容以返回在 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)

【问题讨论】:

  • coalesce(COUNT(exam_items.id),0) as total_used ???
  • 好吧,我也试过 COUNT(exam_items.id) 作为 total_used,但我不得不说 sql 并不是我的强项!
  • 对不起,我明白你的意思了!我已经尝试过了,但仍然没有运气:/
  • 发布问题时,请在问题中说明“它不起作用”的含义 - 不返回任何内容、返回不正确的值、产生错误等。
  • 不相关,但仍然很重要:organisation_id integer REFERENCES organisations(id)

标签: sql postgresql count left-join


【解决方案1】:

修复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,这些函数在没有选择行时返回一个空值。

我的大胆强调。见:

count() 必须位于定义为NOT NULL 的列上(如e.id),或者在示例中连接条件保证NOT NULLe.organisation_ide.item_template_ide.used)的位置。

由于usedboolean 类型,所以表达式e.used = true 是一种噪音,会燃烧成e.used

由于 o.name 未定义 UNIQUE NOT NULL,您可能希望改为 GROUP BY o.idid 是 PK) - 除非您打算折叠具有相同名称的行(包括空)。

先聚合,后加入

如果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(*),并且我们不需要在外部SELECT 中使用GROUP BY

见:

【讨论】:

  • @wildplasser:最好的人错过了那个。这是一个偷偷摸摸的。
  • 我猜,这基本上是风格/品味问题。就个人而言,我永远不会写这样的一行(我希望 ;-) 就像只使用一个参数的 coalesce() 是没有意义的。缺乏相关名称/别名让我感到困惑。
  • @wildplasser:我总是先格式化和清理查询,然后再尝试理解一堆文本。
  • 我也是。一般。 (这就是我喜欢 CTE 的真正原因)但如果我认为我在一次阅读过程中就遇到了问题,我会很高兴。
  • 以防万一您得到 1 而不是 0 作为计数:stackoverflow.com/questions/44155025/…(计数函数中指定的列很重要)。
【解决方案2】:

说清楚,

重要的一行是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

【讨论】:

  • 简单明了??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-05
  • 2015-01-30
  • 2015-02-24
  • 1970-01-01
  • 2017-11-16
  • 1970-01-01
相关资源
最近更新 更多