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()mengabaikanNULLnilai-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 hoursNamun, jika ada kemungkinan semua kolom pilihan memiliki
NULLatau string kosong, bungkus jumlahnya menjadiCOALESCEsekali.
Saya menggunakannumericbukannyafloat, alternatif yang lebih aman untuk meminimalkan kesalahan pembulatan. -
Upaya Anda untuk mendapatkan nilai yang berbeda dari gabungan
usersdantaskssia-sia, karena Anda bergabung dengantasksekali 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
datedaritimestampAnda cukup melakukan cast kedate:tl.created_on::date AS changedayTetapi jauh lebih baik untuk menguji dengan nilai asli di
WHEREklausa atauJOINcondition (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 borderPerhatikan bahwa literal tanggal diubah menjadi
timestampdi00:00hari 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 tasktambahkanWHEREklausa:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id) -
Yang terpenting :
LEFT [OUTER] JOINmempertahankan semua baris di sebelah kiri gabungan. MenambahkanWHEREklausa di kanan tabel dapat membatalkan efek ini. Sebagai gantinya, pindahkan ekspresi filter keJOINklausa. 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,projectsdantask_log_entriessebelum menggabungkan semuanya keusers- tanpa subkueri. -
Alias tabel membuat penulisan kueri yang rumit menjadi lebih mudah.