43

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)
4

2 に答える 2

82

修正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句に移動して、意図したとおりに機能するようにします。このように、これらすべての条件を満たす行のみが最初に結合されます(または、右側のテーブルの列がNULLで埋められます)。あなたがそれを持っていたように、結合された行は、プレーンの場合と同じように、実質的にの後に追加の条件についてテストされ、LEFT JOIN合格しなかった場合は削除されJOINます。

count()そもそもNULLを返すことはありません。この点では、集計関数の例外です。したがって、追加のパラメーターを使用しても、意味がありません。マニュアル:COALESCE(COUNT(col))

を除いてcount、これらの関数は行が選択されていない場合にnull値を返すことに注意してください。

大胆な強調鉱山。見る:

count()定義された列NOT NULL(のようにe.id)、または結合条件が例でNOT NULLe.organisation_id、、、e.item_template_idまたはe.used)を保証する列に存在する必要があります。

usedはタイプなのでboolean、式e.used = trueはただに燃え尽きるノイズですe.used

o.nameは定義されていないため、同じ名前(NULLを含む)の行を折りたたむ場合を除いて、代わりに(PKとして)使用UNIQUE NOT NULLすることをお勧めします。GROUP BY o.idid

最初に集約し、後で参加する

プロセスでのほとんどまたはすべての行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 BYSELECT

見る:

于 2013-03-18T00:30:49.223 に答える
13

明確にするために、

重要な行はGROUP BY MAIN_TABLE、からのNULL値を処理することです。SOME_TABLE

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
于 2015-10-08T16:26:11.567 に答える