Truy vấn có thể được đơn giản hóa thành:
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;
Điều này trả về tất cả người dùng đã từng có bất kỳ tác vụ nào.
Cộng với dữ liệu mỗi dự án và ngày nơi dữ liệu tồn tại trong phạm vi ngày được chỉ định trong task_log_entries
.
Những điểm chính
-
hàm tổng hợp
sum()
bỏ quaNULL
các giá trị.COALESCE()
mỗi hàng không cần thiết nữa ngay sau khi bạn đọc lại phép tính vì sự khác biệt của hai tổng:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
Tuy nhiên, nếu có thể là tất cả các cột của vùng lựa chọn có
NULL
hoặc các chuỗi trống, bọc các tổng vàoCOALESCE
một lần.
Tôi đang sử dụngnumeric
thay vìfloat
, giải pháp thay thế an toàn hơn để giảm thiểu lỗi làm tròn. -
Nỗ lực của bạn để nhận các giá trị khác biệt với sự tham gia của
users
vàtasks
là vô ích, vì bạn tham gia vàotask
một lần nữa xuống sâu hơn. Làm phẳng toàn bộ truy vấn để làm cho nó đơn giản và nhanh hơn. -
tham chiếu vị trí chỉ là một sự tiện lợi về mặt ký hiệu:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3
... làm giống như trong truy vấn ban đầu của bạn.
-
Để lấy
date
từtimestamp
bạn có thể chỉ cần truyền đếndate
:tl.created_on::date AS changeday
Nhưng tốt hơn nhiều là bạn nên kiểm tra với các giá trị gốc trong
WHERE
mệnh đề hoặcJOIN
điều kiện (nếu có thể, và có thể ở đây), vì vậy Postgres có thể sử dụng các chỉ số thuần túy trên cột (nếu có):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
Lưu ý rằng một ký tự ngày được chuyển đổi thành
timestamp
lúc00:00
trong ngày vào thời điểm hiện tại của bạn khu vực . Bạn cần chọn tiếp theo ngày và loại trừ nó như đường viền trên. Hoặc cung cấp ký tự dấu thời gian rõ ràng hơn như'2013-09-22 0:0 +2':: timestamptz
. Thông tin thêm về loại trừ đường viền trên: -
Đối với yêu cầu
every user who has ever been assigned to a task
thêmWHERE
mệnh đề:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
-
Quan trọng nhất :
LEFT [OUTER] JOIN
giữ nguyên tất cả các hàng ở bên trái của phép nối. ThêmWHERE
mệnh đề ở bên phải bảng có thể làm mất tác dụng này. Thay vào đó, hãy di chuyển biểu thức bộ lọc đếnJOIN
mệnh đề. Giải thích thêm ở đây: -
Dấu ngoặc đơn có thể được sử dụng để buộc thứ tự các bảng được nối với nhau. Hiếm khi cần thiết cho các truy vấn đơn giản, nhưng rất hữu ích trong trường hợp này. Tôi sử dụng tính năng này để tham gia
task
,fixins
,projects
vàtask_log_entries
trước khi kết hợp trái tất cả vớiusers
- không có truy vấn con. -
Bí danh bảng giúp việc viết các truy vấn phức tạp dễ dàng hơn.