Kueri mungkin dapat disederhanakan menjadi:
SELECT u.name AS user_name
, p.name AS project_name
, tl.created_on::date AS changeday
, coalesce(sum(nullif(new_value, '')::numeric), 0)
- coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
FROM users u
LEFT JOIN (
tasks t
JOIN fixins f ON f.id = t.fixin_id
JOIN projects p ON p.id = f.project_id
JOIN task_log_entries tl ON tl.task_id = t.id
AND tl.field_id = 18
AND (tl.created_on IS NULL OR
tl.created_on >= '2013-09-08' AND
tl.created_on < '2013-09-09') -- upper border!
) ON t.assignee_id = u.id
WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
GROUP BY 1, 2, 3
ORDER BY 1, 2, 3;
Ini mengembalikan semua pengguna yang pernah memiliki tugas apa pun.
Plus data per proyek dan hari di mana data ada dalam rentang tanggal yang ditentukan di task_log_entries
.
Poin utama
-
Fungsi gabungan
sum()
mengabaikanNULL
nilai-nilai.COALESCE()
per baris tidak diperlukan lagi segera setelah Anda menyusun kembali perhitungan sebagai selisih dua jumlah:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
Namun, jika ada kemungkinan semua kolom pilihan memiliki
NULL
atau string kosong, bungkus jumlahnya menjadiCOALESCE
sekali.
Saya menggunakannumeric
bukannyafloat
, alternatif yang lebih aman untuk meminimalkan kesalahan pembulatan. -
Upaya Anda untuk mendapatkan nilai yang berbeda dari gabungan
users
dantasks
sia-sia, karena Anda bergabung dengantask
sekali lagi lebih jauh ke bawah. Ratakan seluruh kueri untuk membuatnya lebih sederhana dan lebih cepat. -
Ini referensi posisi hanyalah kenyamanan notasi:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3
... melakukan hal yang sama seperti pada kueri awal Anda.
-
Untuk mendapatkan
date
daritimestamp
Anda cukup melakukan cast kedate
:tl.created_on::date AS changeday
Tetapi jauh lebih baik untuk menguji dengan nilai asli di
WHERE
klausa atauJOIN
condition (jika memungkinkan, dan dimungkinkan di sini), sehingga Postgres dapat menggunakan indeks biasa pada kolom (jika tersedia):AND (tl.created_on IS NULL OR tl.created_on >= '2013-09-08' AND tl.created_on < '2013-09-09') -- next day as excluded upper border
Perhatikan bahwa literal tanggal diubah menjadi
timestamp
di00:00
hari ini pada waktu Anda saat ini zona . Anda harus memilih berikutnya hari dan kecualikan itu sebagai batas atas. Atau berikan stempel waktu yang lebih eksplisit seperti'2013-09-22 0:0 +2':: timestamptz
. Lebih lanjut tentang mengecualikan batas atas: -
Untuk kebutuhan
every user who has ever been assigned to a task
tambahkanWHERE
klausa:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
-
Yang terpenting :
LEFT [OUTER] JOIN
mempertahankan semua baris di sebelah kiri gabungan. MenambahkanWHERE
klausa di kanan tabel dapat membatalkan efek ini. Sebagai gantinya, pindahkan ekspresi filter keJOIN
klausa. Penjelasan lebih lanjut di sini: -
Kurung dapat digunakan untuk memaksa urutan tabel bergabung. Jarang diperlukan untuk kueri sederhana, tetapi sangat berguna dalam kasus ini. Saya menggunakan fitur ini untuk bergabung dengan
task
,fixins
,projects
dantask_log_entries
sebelum menggabungkan semuanya keusers
- tanpa subkueri. -
Alias tabel membuat penulisan kueri yang rumit menjadi lebih mudah.