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ỏ quaNULLcá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 hoursTuy nhiên, nếu có thể là tất cả các cột của vùng lựa chọn có
NULLhoặc các chuỗi trống, bọc các tổng vàoCOALESCEmột lần.
Tôi đang sử dụngnumericthay 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
usersvàtaskslà vô ích, vì bạn tham gia vàotaskmộ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
datetừtimestampbạn có thể chỉ cần truyền đếndate:tl.created_on::date AS changedayNhưng tốt hơn nhiều là bạn nên kiểm tra với các giá trị gốc trong
WHEREmệ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 borderLưu ý rằng một ký tự ngày được chuyển đổi thành
timestamplúc00:00trong 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 taskthêmWHEREmệnh đề:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id) -
Quan trọng nhất :
LEFT [OUTER] JOINgiữ nguyên tất cả các hàng ở bên trái của phép nối. ThêmWHEREmệ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 đếnJOINmệ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,projectsvàtask_log_entriestrướ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.