PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Tham gia bên ngoài trái hoạt động như tham gia bên trong

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ỏ qua NULL 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ào COALESCE một lần.
    Tôi đang sử dụng numeric 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 userstasks là vô ích, vì bạn tham gia vào task 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 đến date :

    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ặc JOIN đ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úc 00: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êm WHERE 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êm WHERE 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 đến JOIN 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 , projectstask_log_entries trước khi kết hợp trái tất cả với users - 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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Lỗi giá trị khi nhập dữ liệu vào bảng postgres bằng psycopg2

  2. Tổng quan về các tiện ích mở rộng đáng tin cậy trong PostgreSQL 13

  3. PLINQ trên ConcurrentQueue không phải là đa luồng

  4. Knex.js - Làm thế nào để tạo chỉ mục duy nhất với mệnh đề 'where'?

  5. Kích thước cơ sở dữ liệu PostgreSQL nhỏ hơn sau khi sao lưu / tải trên Heroku