Để giải thích về câu trả lời của @ alci:
PostgreSQL không quan tâm bạn viết thứ gì trong
PostgreQuery hoàn toàn không quan tâm đến thứ tự của các mục trong một WHERE
mệnh đề và chọn chỉ mục và thứ tự thực hiện chỉ dựa trên ước tính chi phí và chọn lọc.
Thứ tự tham gia được viết cũng được bỏ qua cho đến cấu hình join_collapse_limit
; nếu có nhiều kết nối hơn thế, nó sẽ thực hiện chúng theo thứ tự chúng được viết.
Các truy vấn con có thể được thực hiện trước hoặc sau truy vấn có chứa chúng, tùy thuộc vào những gì nhanh nhất, miễn là truy vấn con được thực hiện trước khi truy vấn bên ngoài thực sự cần thông tin. Thông thường trong thực tế, truy vấn con được thực hiện ở giữa hoặc xen kẽ với truy vấn bên ngoài.
Không có gì đảm bảo PostgreSQL thực sự sẽ thực hiện các phần của truy vấn. Họ có thể được tối ưu hóa hoàn toàn đi. Điều này rất quan trọng nếu bạn gọi các hàm có tác dụng phụ.
PostgreSQL sẽ chuyển đổi truy vấn của bạn
PostgreSQL sẽ chuyển đổi nhiều truy vấn trong khi vẫn giữ được các hiệu ứng chính xác tương tự, để làm cho chúng chạy nhanh hơn trong khi không thay đổi kết quả.
Các thuật ngữ bên ngoài truy vấn con có thể bị đẩy xuống truy vấn phụ để chúng thực thi như một phần của truy vấn phụ không phải là nơi bạn đã viết chúng trong truy vấn bên ngoài
Các thuật ngữ trong truy vấn con có thể được kéo lên truy vấn bên ngoài để việc thực hiện chúng được thực hiện như một phần của truy vấn bên ngoài, chứ không phải nơi bạn đã viết chúng trong truy vấn phụ
Truy vấn con có thể, và thường là, làm phẳng thành một liên kết trên bàn bên ngoài. Điều tương tự cũng đúng với những thứ như EXISTS
và NOT EXISTS
truy vấn.
Lượt xem được làm phẳng vào truy vấn sử dụng chế độ xem
Các hàm SQL thường được đưa vào truy vấn gọi
... và có rất nhiều phép biến đổi khác được thực hiện cho các truy vấn, chẳng hạn như đánh giá trước biểu thức không đổi, không tương quan của một số truy vấn con và tất cả các loại thủ thuật lập kế hoạch / tối ưu hóa khác.
Nói chung, PostgreSQL có thể chuyển đổi và viết lại truy vấn của bạn một cách ồ ạt, đến điểm mà mỗi truy vấn này:
select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;
select *
from my_table
where not exists (
select 1
from other_table
where other_table.my_table_id = my_table.id
);
select *
from my_table
where my_table.id not in (
select my_table_id
from other_table
where my_table_id is not null
);
tất cả thường sẽ tạo ra chính xác cùng một kế hoạch truy vấn. (Giả sử tôi đã không phạm phải bất kỳ sai lầm ngớ ngẩn nào ở trên).
Không có gì lạ khi chỉ cố gắng tối ưu hóa một truy vấn để thấy rằng trình hoạch định truy vấn đã tìm ra các thủ thuật bạn đang thử và áp dụng chúng một cách tự động, vì vậy phiên bản tối ưu hóa bằng tay không tốt hơn phiên bản gốc.
Hạn chế
Công cụ lập kế hoạch / trình tối ưu hóa khác xa so với omnicient và bị giới hạn bởi yêu cầu phải tuyệt đối chắc chắn rằng nó không thể thay đổi hiệu ứng của truy vấn, dữ liệu có sẵn để đưa ra quyết định, các quy tắc đã được triển khai và thời gian của CPU nó có thể đủ khả năng để chi tiêu suy nghĩ tối ưu hóa. Ví dụ:
Công cụ lập kế hoạch dựa trên số liệu thống kê được lưu giữ ANALYZE
(thường thông qua autovacuum). Nếu những điều này đã lỗi thời, sự lựa chọn kế hoạch có thể là xấu.
Các số liệu thống kê chỉ là một mẫu, vì vậy chúng có thể gây hiểu nhầm do hiệu ứng lấy mẫu, đặc biệt là nếu mẫu quá nhỏ được lấy. Lựa chọn kế hoạch xấu có thể dẫn đến.
Các thống kê không theo dõi một số loại dữ liệu về bảng, như tương quan giữa các cột. Điều này có thể khiến người lập kế hoạch đưa ra quyết định tồi khi họ cho rằng mọi thứ là độc lập khi họ không.
Công cụ lập kế hoạch dựa trên các tham số chi phí muốn random_page_cost
cho nó biết tốc độ tương đối của các hoạt động khác nhau trên hệ thống cụ thể mà nó được cài đặt. Đây chỉ là hướng dẫn. Nếu họ sai lầm nghiêm trọng, họ có thể dẫn đến các lựa chọn kế hoạch kém.
Bất kỳ truy vấn con nào có LIMIT
hoặc OFFSET
không thể được làm phẳng hoặc có thể bị kéo / đẩy xuống. Điều này không có nghĩa là nó sẽ thực hiện trước khi tất cả các bộ phận của truy vấn bên ngoài, tuy nhiên, hoặc thậm chí là nó sẽ thực hiện ở tất cả .
Các thuật ngữ CTE (các mệnh đề trong WITH
truy vấn) luôn được thực thi toàn bộ, nếu chúng được thực thi hoàn toàn. Chúng không thể được làm phẳng và các điều khoản không thể được đẩy lên hoặc kéo xuống qua hàng rào thuật ngữ CTE. Các thuật ngữ CTE luôn được thực hiện trước khi truy vấn cuối cùng. Đây không phải là hành vi tiêu chuẩn SQL , nhưng nó được ghi lại như cách PostgreQuery thực hiện.
PostgreQuery có một khả năng hạn chế để tối ưu hóa các truy vấn trên các bảng, security_barrier
khung nhìn và các loại quan hệ đặc biệt khác
PostgreQuery sẽ không nội tuyến một hàm được viết bằng bất cứ thứ gì ngoại trừ SQL đơn giản, cũng không thực hiện pullup / đẩy xuống giữa nó và truy vấn bên ngoài.
Công cụ lập kế hoạch / tối ưu hóa thực sự ngu ngốc về việc chọn các chỉ mục biểu thức và về sự khác biệt loại dữ liệu tầm thường giữa chỉ mục và biểu thức.
Tấn nhiều hơn, quá.
Truy vấn của bạn
Trong trường hợp truy vấn của bạn:
select 1
from workdays day
where day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30'
and day.offer_id in (
select offer.offer_day
from offer
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
)
không có gì ngăn cản nó được làm phẳng thành một truy vấn đơn giản hơn với một tập hợp các phép nối bổ sung, và rất có khả năng nó sẽ xảy ra.
Nó có thể sẽ bật ra một cái gì đó như (chưa được kiểm tra, rõ ràng):
select 1
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
and day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30';
PostgreSQL sau đó sẽ tối ưu hóa thứ tự tham gia và các phương thức tham gia dựa trên ước tính số lượng chọn lọc và số hàng và các chỉ mục có sẵn. Nếu những điều này phản ánh hợp lý thực tế thì nó sẽ tham gia và chạy các mục mệnh đề where theo thứ tự nào là tốt nhất - thường trộn chúng lại với nhau, do đó, một chút về điều này, sau đó quay lại phần đầu tiên, sau đó quay lại phần đầu tiên v.v.
Làm thế nào để xem những gì trình tối ưu hóa đã làm
Bạn không thể thấy SQL mà PostgreQuery tối ưu hóa truy vấn của bạn, bởi vì nó chuyển đổi SQL thành biểu diễn cây truy vấn nội bộ sau đó sửa đổi điều đó. Bạn có thể kết xuất kế hoạch truy vấn và so sánh nó với các truy vấn khác.
Không có cách nào để "khai thác" kế hoạch truy vấn đó hoặc cây kế hoạch nội bộ trở lại SQL.
http://explain.depesz.com/ có một người trợ giúp kế hoạch truy vấn khá. Nếu bạn hoàn toàn mới đối với các kế hoạch truy vấn, v.v. (trong trường hợp đó tôi rất ngạc nhiên là bạn đã thực hiện được điều này qua bài viết này) thì PGAdmin có trình xem kế hoạch truy vấn đồ họa cung cấp ít thông tin hơn nhưng đơn giản hơn.
Đọc liên quan:
Khả năng đẩy / kéo và làm phẳng tiếp tục cải thiện trong mỗi bản phát hành . PostgreSQL thường đúng về các quyết định kéo lên / đẩy xuống / làm phẳng, nhưng không phải lúc nào cũng vậy, vì vậy đôi khi bạn phải (ab) sử dụng CTE hoặc OFFSET 0
hack. Nếu bạn tìm thấy một trường hợp như vậy, báo cáo một lỗi kế hoạch truy vấn.
Nếu bạn thực sự, thực sự quan tâm, bạn cũng có thể sử dụng debug_print_plans
tùy chọn để xem gói truy vấn thô, nhưng tôi hứa bạn không muốn đọc nó. Có thật không.