WHERE 절은 작성된 순서대로 적용됩니까?


36

큰 테이블 (37 백만 행)을 들여다보고 쿼리에서 작업이 실행되는 순서에 대한 질문을 최적화하려고합니다.

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')
    )

있습니까 WHERE날짜 범위에 대한 조항은 부질 전에 실행? 더 빠른 실행을 위해 가장 제한적인 절을 다른 절에 대한 큰 루프를 피하기 위해 가장 좋은 방법입니까?

이제 쿼리를 실행하는 데 많은 시간이 걸립니다.

답변:


68

@alci의 답변을 자세히 설명하려면 다음을 수행하십시오.

PostgreSQL은 작성 순서를 신경 쓰지 않습니다.

  • PostgreSQL은 WHERE절의 항목 순서에 전혀 신경 쓰지 않으며 비용 및 선택성 추정만으로 인덱스와 실행 순서를 선택합니다.

  • 결합이 작성되는 순서는 구성된 구성까지 무시됩니다 join_collapse_limit. 그보다 많은 조인이 있으면 작성된 순서대로 실행됩니다.

  • 외부 쿼리가 실제로 정보를 필요로하기 전에 하위 쿼리가 실행되는 한 하위 쿼리는 가장 빠른 내용에 따라 쿼리가 포함 된 쿼리 전후에 실행될 수 있습니다. 실제로는 하위 쿼리가 중간에서 일종의 실행되거나 외부 쿼리와 인터리브되는 경우가 종종 있습니다.

  • PostgreSQL이 실제로 쿼리의 일부를 실행한다는 보장은 없습니다. 완전히 최적화 할 수 있습니다. 부작용이있는 함수를 호출 할 때 중요합니다.

PostgreSQL은 쿼리를 변환합니다

PostgreSQL은 결과를 변경하지 않고 더 빠르게 실행하기 위해 동일한 효과를 유지하면서 쿼리를 크게 변환합니다.

  • 하위 쿼리 외부의 용어는 하위 쿼리 로 푸시 될 수 있으므로 외부 쿼리에서 작성하지 않은 하위 쿼리의 일부로 실행됩니다.

  • 하위 쿼리의 용어를 외부 쿼리 로 끌어 올 수 있으므로 하위 쿼리에서 해당 용어 를 쓴 곳이 아니라 외부 쿼리의 일부로 실행됩니다.

  • 하위 쿼리 캔, 그리고 자주, 평평 외부 테이블에 조인으로. 동일은 같은 것들의 사실 EXISTSNOT 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원시 쿼리 계획을 볼 수있는 옵션을 사용할 수 있지만, 나는 당신이 그것을 읽고 싶지 않다고 약속합니다. 정말.


와우 ... 꽤 완전한 답변 :-) Postgresql (Oracle과 같은 다른 잘 알려진 DB 엔진과 마찬가지로) 계획이 느린 경우 중 하나는 열 또는 여러 관련 조인 간의 상관 관계입니다. 실제로는 수천 개가있을 때 계획 의이 시점에 몇 개의 행 만 있다고 생각하여 중첩 루프를 수행하게됩니다. 이러한 종류의 쿼리를 최적화하는 한 가지 방법은 'set enable_nestloop = off;'입니다. 쿼리 기간 동안.
alci

간단한 7 where 절 쿼리에서 v9.5.5가 적용 가능한지 확인하기 전에 TO_DATE를 적용하려고하는 상황에 부딪 쳤습니다. 주문이 중요합니다.
user1133275

이 경우 계산 비용 추정치가 동일하기 때문에 우연히 만 작동했습니다. PostgreSQL은 to_date이후 버전에서 확인하기 전에 또는 옵티 마이저 통계 변경으로 인해 여전히 실행 하기로 결정할 수 있습니다. 점검 후에 만 ​​실행되어야하는 기능 전에 점검 을 안정적으로 실행 하려면 CASE명령문을 사용하십시오 .
Craig Ringer

내가 한 가장 큰 답변 한 적이 SO 볼! 엄지 손가락!
62mkv

간단한 쿼리 추가 order by로 인해 쿼리 실행이없는 경우보다 쿼리 실행 속도가 훨씬 빨라졌습니다 order by. 이것이 조인으로 쿼리를 작성하는 것과 같은 방식으로 쿼리를 작성하는 이유 중 하나입니다. 그것은 may be실행 ... 큰 대답!
Greg0ry

17

SQL은 선언적 언어입니다. 수행 방법이 아니라 원하는 것을 말하십시오. RDBMS는 실행 계획이라는 쿼리 실행 방법을 선택합니다.

옛날에 (5-10 년 전) 쿼리 작성 방식이 실행 계획에 직접적인 영향을 주었지만 요즘 대부분의 SQL 데이터베이스 엔진은 계획에 비용 기반 최적화 프로그램을 사용합니다. 즉, 데이터베이스 개체에 대한 통계를 기반으로 쿼리를 실행하는 다양한 전략을 평가하고 가장 적합한 전략을 선택합니다.

대부분의 경우 실제로 가장 좋은 방법이지만 때로는 DB 엔진이 잘못된 선택을하여 쿼리 속도가 느려질 수 있습니다.


일부 RDBMS에서 쿼리 순서는 여전히 중요하지만 고급 단계의 경우 실제로는 물론 이론에서도 사실입니다. 쿼리 플래너가 잘못된 실행 순서 선택을 선택할 때 일반적으로 쿼리 조인을보다 효율적인 방향으로 푸시 할 수있는 쿼리 힌트가 있습니다 (예 : WITH(INDEX(<index>))특정 조인에 대한 인덱스 선택을 강제하는 MSSQL).
David Spillett

문제는 일부 인덱스가 date_day실제로 존재 하는지 여부 입니다. 없는 경우 옵티 마이저에는 비교할 계획이 없습니다.
jkavalik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.