@alci의 답변을 자세히 설명하려면 다음을 수행하십시오.
PostgreSQL은 작성 순서를 신경 쓰지 않습니다.
PostgreSQL은 WHERE
절의 항목 순서에 전혀 신경 쓰지 않으며 비용 및 선택성 추정만으로 인덱스와 실행 순서를 선택합니다.
결합이 작성되는 순서는 구성된 구성까지 무시됩니다 join_collapse_limit
. 그보다 많은 조인이 있으면 작성된 순서대로 실행됩니다.
외부 쿼리가 실제로 정보를 필요로하기 전에 하위 쿼리가 실행되는 한 하위 쿼리는 가장 빠른 내용에 따라 쿼리가 포함 된 쿼리 전후에 실행될 수 있습니다. 실제로는 하위 쿼리가 중간에서 일종의 실행되거나 외부 쿼리와 인터리브되는 경우가 종종 있습니다.
PostgreSQL이 실제로 쿼리의 일부를 실행한다는 보장은 없습니다. 완전히 최적화 할 수 있습니다. 부작용이있는 함수를 호출 할 때 중요합니다.
PostgreSQL은 쿼리를 변환합니다
PostgreSQL은 결과를 변경하지 않고 더 빠르게 실행하기 위해 동일한 효과를 유지하면서 쿼리를 크게 변환합니다.
하위 쿼리 외부의 용어는 하위 쿼리 로 푸시 될 수 있으므로 외부 쿼리에서 작성하지 않은 하위 쿼리의 일부로 실행됩니다.
하위 쿼리의 용어를 외부 쿼리 로 끌어 올 수 있으므로 하위 쿼리에서 해당 용어 를 쓴 곳이 아니라 외부 쿼리의 일부로 실행됩니다.
하위 쿼리 캔, 그리고 자주, 평평 외부 테이블에 조인으로. 동일은 같은 것들의 사실 EXISTS
및 NOT EXISTS
쿼리.
뷰는 뷰를 사용하는 쿼리로 병합됩니다.
SQL 함수는 종종 호출 쿼리에 인라인됩니다.
... 상 수식 사전 평가, 일부 하위 쿼리의 상관 해제, 모든 종류의 다른 플래너 / 최적화 기 트릭과 같은 쿼리에 대한 다른 많은 변환이 있습니다.
일반적으로 PostgreSQL은 다음과 같은 각 쿼리 지점까지 쿼리를 대량으로 변환하고 다시 작성할 수 있습니다.
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
);
일반적으로 모두 정확히 동일한 쿼리 계획을 생성합니다. (어쨌든 위의 어리석은 실수를하지 않았다고 가정).
쿼리 플래너가 이미 시도하고있는 트릭을 알아 낸 후 자동으로 적용한 것을 찾기 위해 쿼리를 최적화하려고 시도하는 것은 드문 일이 아니므로 수동으로 최적화 된 버전이 원본보다 나쁩니다.
한계
플래너 / 최적화 프로그램은 전능 한 것이 아니며 쿼리의 영향, 의사 결정에 사용할 수있는 데이터, 구현 된 규칙 및 CPU 시간을 변경할 수 없다는 확신이 있어야합니다. 최적화에 대해 숙고 할 수 있습니다. 예를 들면 다음과 같습니다.
플래너는 ANALYZE
(보통 autovacuum을 통해) 유지되는 통계에 의존합니다 . 구식 인 경우 계획 선택이 잘못 될 수 있습니다.
통계는 표본 일 뿐이므로 표본 추출 효과로 인해 잘못된 결과를 초래할 수 있습니다 (특히 표본이 너무 작은 경우). 잘못된 계획을 선택할 수 있습니다.
통계는 열 간의 상관 관계와 같이 테이블에 대한 일부 종류의 데이터를 추적하지 않습니다. 이로 인해 플래너는 그렇지 않은 경우 독립적 인 것으로 가정 할 때 잘못된 결정을 내릴 수 있습니다.
플래너 random_page_cost
는 설치된 특정 시스템에서 다양한 작업의 상대 속도를 알려주 는 비용 매개 변수에 의존 합니다. 이들은 가이드 일뿐입니다. 그들이 잘못하면 계획을 잘못 선택할 수 있습니다.
가 LIMIT
있거나 포함 된 하위 쿼리는 OFFSET
병합되거나 풀업 / 푸시 다운 될 수 없습니다. 그렇다고 외부 쿼리의 모든 부분보다 먼저 실행되거나 전혀 실행되지 는 않습니다 .
CTE 용어 ( WITH
쿼리 의 절 )는 전혀 실행되지 않으면 항상 전체적으로 실행됩니다. 그것들은 평평해질 수 없으며, CTE 기간 장벽을 넘어서서 항을 올리거나 내릴 수 없습니다. CTE 용어는 항상 최종 쿼리 전에 실행됩니다. 이것은 SQL 표준 이 아닌 동작이지만 PostgreSQL이 어떻게 작동하는지 문서화되어 있습니다.
PostgreSQL에는 외래 테이블, security_barrier
뷰 및 기타 특정 종류의 관계 에 대한 쿼리를 최적화하는 기능이 제한되어 있습니다.
PostgreSQL은 일반 SQL 이외의 것으로 작성된 함수를 인라인하지 않으며 함수와 외부 쿼리 사이에 풀업 / 푸시 다운을 수행하지 않습니다.
플래너 / 최적화 기는 실제로 표현식 인덱스 선택 및 인덱스와 표현식 간의 사소한 데이터 유형 차이에 대해 바보입니다.
더 톤.
당신의 질문
쿼리의 경우 :
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')
)
추가 조인 세트를 사용하여 더 간단한 쿼리로 병합하는 것을 막을 수는 없습니다.
아마 다음과 같은 것으로 판명 될 것입니다 :
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은 선택성 및 행 수 추정치 및 사용 가능한 인덱스를 기반으로 결합 순서 및 결합 방법을 최적화합니다. 이것들이 현실을 합리적으로 반영한다면 조인을 수행하고 where 절 엔트리를 가장 좋은 순서대로 실행합니다. 등
옵티마이 저가 수행 한 작업을 보는 방법
PostgreSQL이 쿼리를 최적화하는 SQL을 볼 수 없습니다. SQL을 내부 쿼리 트리 표현으로 변환 한 다음 수정하기 때문입니다. 쿼리 계획을 덤프하여 다른 쿼리와 비교할 수 있습니다 .
해당 쿼리 계획 또는 내부 계획 트리를 다시 SQL로 "분리"할 수있는 방법이 없습니다.
http://explain.depesz.com/ 에는 적절한 쿼리 계획 도우미가 있습니다. 쿼리 계획 등에 완전히 익숙하지 않은 경우 (이 게시물을 통해이 기사를 작성 한 것에 놀랐습니다) PgAdmin에는 훨씬 적은 정보를 제공하지만 더 간단한 그래픽 쿼리 계획 뷰어가 있습니다.
관련 독서 :
푸시 다운 / 풀업 및 병합 기능 은 각 릴리스에서 계속 향상됩니다 . PostgreSQL은 일반적으로 풀업 / 푸시 다운 / 플랫 닝 결정에 적합하지만 항상 그런 것은 아니므로 때때로 CTE 나 OFFSET 0
핵을 사용해야합니다 . 이러한 경우를 찾으면 쿼리 플래너 버그를보고하십시오.
당신이 정말로, 정말로 예리한 당신은 또한 debug_print_plans
원시 쿼리 계획을 볼 수있는 옵션을 사용할 수 있지만, 나는 당신이 그것을 읽고 싶지 않다고 약속합니다. 정말.