Tại sao?
Truy vấn không thể sử dụng chỉ mục trên chính. Bạn sẽ cần một chỉ mục trên bảng locations
, nhưng địa chỉ bạn có trên bảng addresses
.
Bạn có thể xác minh khiếu nại của tôi bằng cách cài đặt:
SET enable_seqscan = off;
(Chỉ trong phiên của bạn và chỉ để gỡ lỗi. Không bao giờ sử dụng nó trong sản xuất.) Nó không giống như chỉ mục sẽ đắt hơn quét tuần tự, không có cách nào để Postgres sử dụng nó cho truy vấn của bạn cả .
Bên cạnh:[INNER] JOIN ... ON true
chỉ là một cách nói khó hiểu CROSS JOIN ...
Tại sao chỉ mục được sử dụng sau khi xóa ORDER
và LIMIT
?
Bởi vì Postgres có thể viết lại biểu mẫu đơn giản này thành:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Bạn sẽ thấy cùng một kế hoạch truy vấn. (Ít nhất tôi làm được trong các bài kiểm tra của tôi trên Postgres 9.5.)
Giải pháp
Bạn cần một chỉ mục trên locations.postalcode
. Và trong khi sử dụng LIKE
hoặc ILIKE
bạn cũng cần mang theo biểu thức được lập chỉ mục (postalcode
) ở bên trái bên của nhà điều hành. ILIKE
được triển khai với toán tử ~~*
và toán tử này không có COMMUTATOR
(một điều cần thiết hợp lý), vì vậy không thể lật các toán hạng xung quanh. Giải thích chi tiết trong các câu trả lời liên quan sau:
- PostgreSQL có thể lập chỉ mục các cột không?
- PostgreSQL - mảng văn bản chứa giá trị tương tự như
- Có cách nào để lập chỉ mục hữu ích cột văn bản chứa các mẫu regex không?
Một giải pháp là sử dụng toán tử tương tự trigram %
hoặc nghịch đảo của nó, toán tử khoảng cách <->
ở hàng xóm gần nhất thay vào đó là truy vấn (mỗi là dấu phẩy cho chính nó, vì vậy các toán hạng có thể chuyển đổi vị trí tùy ý):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Tìm postalcode
tương tự nhất cho mỗi addresses
và sau đó kiểm tra xem postalcode
đó thực sự khớp hoàn toàn.
Bằng cách này, một postalcode
dài hơn sẽ tự động được ưu tiên vì nó giống hơn (khoảng cách nhỏ hơn) so với postalcode
ngắn hơn điều đó cũng phù hợp.
Một chút không chắc chắn vẫn còn. Tùy thuộc vào các mã bưu chính có thể có, có thể có dương tính giả do các bát quái khớp với các phần khác của chuỗi. Không có đủ thông tin trong câu hỏi để nói thêm.
Đây , [INNER] JOIN
thay vì CROSS JOIN
có lý, vì chúng tôi thêm một điều kiện tham gia thực tế.
Vì vậy:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);