Perbaiki LEFT JOIN
Ini akan berhasil:
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;
Anda memiliki LEFT [OUTER] JOIN
tapi nanti WHERE
kondisi membuatnya bertindak seperti [INNER] JOIN
biasa .
Pindahkan kondisi ke JOIN
klausa untuk membuatnya bekerja sebagaimana dimaksud. Dengan cara ini, hanya baris yang memenuhi semua kondisi ini yang akan digabungkan (atau kolom dari kanan tabel diisi dengan NULL). Seperti yang Anda alami, baris yang digabungkan diuji untuk kondisi tambahan secara virtual setelah LEFT JOIN
dan dihapus jika tidak lulus, seperti dengan JOIN
plain biasa .
count()
tidak pernah mengembalikan NULL untuk memulai. Ini pengecualian di antara fungsi agregat dalam hal ini. Oleh karena itu, tidak pernah masuk akal, bahkan dengan parameter tambahan. Panduan:COALESCE(COUNT(col))
Perlu diperhatikan bahwa kecuali
count
, fungsi-fungsi ini mengembalikan nilai nol saat tidak ada baris yang dipilih.
Penekanan saya yang berani. Lihat:
- Hitung jumlah atribut yang NULL untuk satu baris
count()
harus pada kolom yang ditentukan NOT NULL
(seperti e.id
), atau di mana kondisi join menjamin NOT NULL
(e.organisation_id
, e.item_template_id
, atau e.used
) dalam contoh.
Sejak used
adalah ketik boolean
, ekspresi e.used = true
adalah kebisingan yang membakar hanya e.used
.
Sejak o.name
tidak didefinisikan UNIQUE NOT NULL
, Anda mungkin ingin GROUP BY o.id
sebagai gantinya (id
menjadi PK) - kecuali jika Anda berniat untuk melipat baris dengan nama yang sama (termasuk NULL).
Agregat dulu, gabung nanti
Jika sebagian besar atau semua baris exam_items
dihitung dalam proses, kueri yang setara ini biasanya jauh lebih cepat/lebih murah:
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;
(Ini dengan asumsi bahwa Anda tidak ingin melipat baris dengan nama yang sama seperti yang disebutkan di atas - kasus biasa.)
Sekarang kita dapat menggunakan count(*)
yang lebih cepat / lebih sederhana di subquery, dan kita tidak memerlukan GROUP BY
di bagian luar SELECT
.
Lihat:
- Beberapa panggilan array_agg() dalam satu kueri