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

Sử dụng cùng một cột nhiều lần trong mệnh đề WHERE

Đây là một trường hợp của phép chia quan hệ. Tôi đã thêm thẻ.

Chỉ mục

Giả sử ràng buộc PK hoặc DUY NHẤT trên USER_PROPERTY_MAP(property_value_id, user_id) - cột theo thứ tự này để thực hiện các truy vấn của tôi nhanh chóng. Có liên quan:

  • Chỉ số tổng hợp có tốt cho các truy vấn trên trường đầu tiên không?

Bạn cũng phải có chỉ mục trên PROPERTY_VALUE(value, property_name_id, id) . Một lần nữa, các cột theo thứ tự này. Thêm cột cuối cùng id chỉ khi bạn nhận được các bản quét chỉ lập chỉ mục từ nó.

Đối với một số thuộc tính nhất định

Có nhiều cách để giải quyết nó. Đây phải là một trong những cách đơn giản và nhanh nhất cho chính xác hai thuộc tính:

SELECT u.*
FROM   users             u
JOIN   user_property_map up1 ON up1.user_id = u.id
JOIN   user_property_map up2 USING (user_id)
WHERE  up1.property_value_id =
      (SELECT id FROM property_value WHERE property_name_id = 1 AND value = '101')
AND    up2.property_value_id =
      (SELECT id FROM property_value WHERE property_name_id = 2 AND value = '102')
-- AND    u.user_name = 'user1'  -- more filters?
-- AND    u.city = 'city1'

Không truy cập bảng PROPERTY_NAME , vì bạn dường như đã phân giải tên thuộc tính thành ID, theo truy vấn mẫu của bạn. Nếu không, bạn có thể thêm một tham gia vào PROPERTY_NAME trong mỗi truy vấn con.

Chúng tôi đã tập hợp một kho kỹ thuật theo câu hỏi liên quan này:

  • Cách lọc kết quả SQL theo quan hệ có nhiều lần

Đối với một số thuộc tính không xác định

@Mike và @Valera có các truy vấn rất hữu ích trong câu trả lời tương ứng của họ. Để làm cho điều này trở nên năng động hơn nữa :

WITH input(property_name_id, value) AS (
      VALUES  -- provide n rows with input parameters here
        (1, '101')
      , (2, '102')
      -- more?
      ) 
SELECT *
FROM   users u
JOIN  (
   SELECT up.user_id AS id
   FROM   input
   JOIN   property_value    pv USING (property_name_id, value)
   JOIN   user_property_map up ON up.property_value_id = pv.id
   GROUP  BY 1
   HAVING count(*) = (SELECT count(*) FROM input)
   ) sub USING (id);

Chỉ thêm / bớt các hàng khỏi VALUES biểu hiện. Hoặc xóa WITH và mệnh đề JOIN cho không có bộ lọc thuộc tính ở tất cả.

Vấn đề với loại truy vấn này (tính tất cả các kết quả phù hợp từng phần) là hiệu suất . Truy vấn đầu tiên của tôi ít năng động hơn, nhưng thường nhanh hơn đáng kể. (Chỉ cần kiểm tra với EXPLAIN ANALYZE .) Đặc biệt là đối với các bảng lớn hơn và số lượng thuộc tính ngày càng tăng.

Tốt nhất của cả hai thế giới?

Giải pháp này với CTE đệ quy sẽ là một sự thỏa hiệp tốt: nhanh động:

WITH RECURSIVE input AS (
   SELECT count(*)     OVER () AS ct
        , row_number() OVER () AS rn
        , *
   FROM  (
      VALUES  -- provide n rows with input parameters here
        (1, '101')
      , (2, '102')
      -- more?
      ) i (property_name_id, value)
   )
 , rcte AS (
   SELECT i.ct, i.rn, up.user_id AS id
   FROM   input             i
   JOIN   property_value    pv USING (property_name_id, value)
   JOIN   user_property_map up ON up.property_value_id = pv.id
   WHERE  i.rn = 1

   UNION ALL
   SELECT i.ct, i.rn, up.user_id
   FROM   rcte              r
   JOIN   input             i ON i.rn = r.rn + 1
   JOIN   property_value    pv USING (property_name_id, value)
   JOIN   user_property_map up ON up.property_value_id = pv.id
                              AND up.user_id = r.id
   )
SELECT u.*
FROM   rcte  r
JOIN   users u USING (id)
WHERE  r.ct = r.rn;          -- has all matches

dbfiddle tại đây

Hướng dẫn về CTE đệ quy.

Sự phức tạp tăng thêm không trả cho các bảng nhỏ mà chi phí bổ sung lớn hơn bất kỳ lợi ích nào hoặc sự khác biệt là không đáng kể để bắt đầu. Nhưng quy mô tốt hơn nhiều và ngày càng vượt trội hơn so với kỹ thuật "đếm" với các bảng ngày càng tăng và số lượng bộ lọc thuộc tính ngày càng tăng.

Kỹ thuật đếm phải truy cập tất cả các hàng trong user_property_map cho tất cả các bộ lọc thuộc tính nhất định, trong khi truy vấn này (cũng như truy vấn đầu tiên) có thể loại bỏ sớm những người dùng không liên quan.

Tối ưu hóa hiệu suất

Với thống kê bảng hiện tại (cài đặt hợp lý, autovacuum đang chạy), Postgres có kiến ​​thức về "các giá trị phổ biến nhất" trong mỗi cột và sẽ sắp xếp lại thứ tự các phép nối trong truy vấn đầu tiên để đánh giá các bộ lọc thuộc tính chọn lọc nhất trước (hoặc ít nhất không phải các bộ lọc thuộc tính ít chọn lọc nhất). Lên đến một giới hạn nhất định:join_collapse_limit . Có liên quan:

  • Postgresql join_collapse_limit và thời gian để lập kế hoạch truy vấn
  • Tại sao một thay đổi nhỏ trong cụm từ tìm kiếm lại làm chậm truy vấn đến vậy?

Không thể can thiệp "deus-ex-machina" này với truy vấn thứ 3 (CTE đệ quy). Để giúp hiệu suất (có thể là nhiều), trước tiên bạn phải tự mình đặt các bộ lọc chọn lọc hơn. Nhưng ngay cả với thứ tự trong trường hợp xấu nhất, nó vẫn sẽ hoạt động tốt hơn các truy vấn đếm.

Có liên quan:

  • Kiểm tra các mục tiêu thống kê trong PostgreSQL

Nhiều chi tiết đẫm máu hơn:

  • Chỉ mục từng phần của PostgreSQL không được sử dụng khi được tạo trên bảng có dữ liệu hiện có

Giải thích thêm trong sách hướng dẫn:

  • Số liệu thống kê được Người lập kế hoạch sử dụng


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Xuất Bảng AWS Postgres RDS sang AWS S3

  2. Lumen - Tạo kết nối cơ sở dữ liệu trong thời gian chạy

  3. Không thể kết nối với máy chủ postgres trong một docker từ một ứng dụng dày đặc

  4. Postgres chỉ ngẫu nhiên ngừng hoạt động (Rails, PGSQL.5432)

  5. Hạn chế tên cột bảng Postgres?