CTE, 하위 쿼리, 임시 테이블 또는 테이블 변수간에 성능 차이가 있습니까?


222

이 우수한에서 SO 질문 , 차이 CTEsub-queries논의되었다.

나는 구체적으로 묻고 싶다 :

다음 중 각각의 상황이 더 효율적 / 빠른 상황은 무엇입니까?

  • CTE
  • 하위 쿼리
  • 임시 테이블
  • 테이블 변수

전통적으로, 나는 많은 얽힌 하위 쿼리보다 읽기 쉬운 것처럼 보이기 때문에 temp tables개발에 많은 것을 사용했습니다 stored procedures.

Non-recursive CTEs 데이터 세트를 잘 캡슐화하고 읽을 수 있지만 항상 더 나은 성능을 발휘할 수있는 특정 상황이 있습니까? 또는 가장 효율적인 솔루션을 찾기 위해 항상 다른 옵션을 사용하는 경우가 있습니까?


편집하다

최근에 효율성 측면에서 임시 테이블은 관련 히스토그램 즉 통계가 있기 때문에 최선의 선택이라고 들었습니다.


4
일반적인 답변 : 상황에 따라 다릅니다. 그리고 그것은 많은 요인에 달려 있으며, 어떤 상황에서는 일반적인 진술이 틀릴 수 있습니다. 기본적으로 : 테스트하고 측정해야합니다. 어떤 것이 가장 적합한 지 확인하십시오!
marc_s

@marc_s-좋아; 아마도이 질문은 주관적인 것으로 폐쇄되어야합니까? SO에 대한 많은 SQL 질문이 주관적인 것으로 판단 될 수 있습니다.
whytheq

1
너무 광범위해서 폐쇄 될 수도 있고, SQL의 많은 것들과 주제가 실제로 그에 대한 대답을 얻을 것이라는 데 동의합니다 . 때때로 하나 또는 둘 이상의 세 가지 기준으로 결정을 내릴 수 있지만 여기에서 귀하의 질문으로 건전한 조언을 제공하는 것은 불가능합니다-테이블 구조, 해당 테이블의 데이터, 사용중인 쿼리, 색인 전략과 훨씬 더 ....
marc_s

@marc_s 좀 더 구체적이고 좁게 만들기 위해 OP에 대한 가능한 수정 사항에 대한 조언이 있습니까?
whytheq

이 질문은 SQL Server에만 해당됩니다. postgres와 같은 다른 DB의 경우 CTE는 종종 동등한 하위 쿼리보다 훨씬 느립니다 ( http://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/ 참조 )
Jay

답변:


243

SQL은 절차 적 언어가 아닌 선언적 언어입니다. 즉, 원하는 결과를 설명하기 위해 SQL 문을 구성합니다. 작업 수행 방법 을 SQL 엔진에 알리지 않습니다 .

일반적으로 SQL 엔진과 SQL 최적화 프로그램이 최상의 쿼리 계획을 찾도록하는 것이 좋습니다. SQL 엔진을 개발하기 위해 많은 노력을 기울이고 있으므로 엔지니어가 어떻게해야하는지 알고 있습니다.

물론 쿼리 계획이 최적이 아닌 상황이 있습니다. 그런 다음 쿼리 힌트를 사용하고, 쿼리를 재구성하고, 통계를 업데이트하고, 임시 테이블을 사용하고, 인덱스를 추가하여 성능을 향상 시키려고합니다.

당신의 질문에 관해서는. CTE와 하위 쿼리의 성능은 이론적으로 쿼리 옵티 마이저에 동일한 정보를 제공하므로 이론적으로 동일해야합니다. 한 가지 차이점은 한 번 이상 사용 된 CTE를 한 번 쉽게 식별하고 계산할 수 있다는 것입니다. 그런 다음 결과를 저장하고 여러 번 읽을 수 있습니다. 불행히도 SQL Server는이 기본 최적화 방법을 사용하지 않는 것 같습니다 (이 공통 하위 쿼리 제거를 호출 할 수 있음).

쿼리 실행 방법에 대한 추가 지침을 제공하므로 임시 테이블은 다른 문제입니다. 한 가지 큰 차이점은 옵티마이 저가 임시 테이블의 통계를 사용하여 쿼리 계획을 설정할 수 있다는 것입니다. 이로 인해 성능이 향상 될 수 있습니다. 또한 두 번 이상 사용되는 복잡한 CTE (하위 쿼리)가있는 경우 임시 테이블에 저장하면 성능이 향상 될 수 있습니다. 쿼리는 한 번만 실행됩니다.

귀하의 질문에 대한 답변은 특히 정기적으로 실행되는 복잡한 쿼리에 대해 기대하는 성능을 얻기 위해 놀아야한다는 것입니다. 이상적인 세계에서 쿼리 최적화 프로그램은 완벽한 실행 경로를 찾습니다. 자주 사용하지만 성능을 향상시킬 수있는 방법을 찾을 수 있습니다.


11
이 영역의 향후 개선 가능성에 대한 일부 Microsoft Research는 "쿼리 처리를위한 유사한 하위 표현의 효율적인 탐색"(영문)에서 볼 수 있습니다.
Martin Smith

3
2007 년에이 논문이 발표되었다는 사실을 감안할 때, SQL Server 2012에이 논문이 포함되어 있는지 여부에 대한 아이디어가 있습니까?
Gordon Linoff

3
좋은 답변입니다! 강조 할 점 : SQL은 선언적 언어이며 데이터를 가져 오는 방법을 제어하지 않습니다. 따라서 성능 / 속도는 쿼리마다 다릅니다.
Simcha Khabinsky 2014 년

2
@RGS. . . 임시 테이블의 인덱스는 영구 테이블의 인덱스와 마찬가지로 이러한 인덱스를 활용할 수있는 쿼리를 확실히 향상시킵니다. 그러나 하위 쿼리를 임시 테이블로 구체화하면 원래 테이블의 인덱스 이점을 잃을 수 있습니다.
Gordon Linoff

2
@RGS. . 데이터베이스 엔진이 복잡한 쿼리를 실행하는 동안 하위 쿼리 / CTE를 구체화하면 구체화에 인덱스를 추가하지 않습니다. 임시 테이블을 사용하여 수동으로이를 수행 할 수 있습니다.
Gordon Linoff

77

규칙이 없습니다. CTE가 더 읽기 쉽고 성능 문제가 없는 한 사용합니다 .이 경우 CTE가 문제라고 생각하기보다는 실제 문제를 조사한 후 다른 접근 방식을 사용하여 다시 작성하려고 시도합니다. 쿼리에 의도를 선언적으로 선언하는 방식보다 일반적으로 문제가 더 많습니다.

CTE를 풀거나 하위 쿼리를 제거하고 #temp 테이블로 바꾸고 지속 시간을 줄일 수있는 경우가 있습니다. 오래된 통계, 정확한 통계를 얻을 수 없거나 (예 : 테이블 반환 함수에 참여할 수 없음), 병렬 처리 또는 쿼리의 복잡성으로 인해 최적의 계획을 생성 할 수없는 등의 다양한 요인 때문일 수 있습니다 ( 이 경우이를 해제하면 옵티 마이저에게 전투 기회가 제공 될 수 있습니다. 그러나 #temp 테이블 생성과 관련된 I / O가 CTE를 사용하여 특정 계획 형태를 덜 매력적으로 만들 수있는 다른 성능 측면을 능가하는 경우도 있습니다.

솔직히 말해서 귀하의 질문에 "올바른"답변을 제공하기에는 너무 많은 변수가 있습니다. 쿼리가 한 접근 방식 또는 다른 접근 방식을 선호하는 시점을 알 수있는 예측 가능한 방법은 없습니다. 이론적으로 CTE 또는 단일 하위 쿼리에 대해 동일한 의미론 이 정확히 동일한 방식으로 실행 되어야한다는 사실 만 알고 있으면 됩니다. 이것이 사실이 아닌 경우를 제시하면 귀하의 질문이 더 가치가 있다고 생각합니다. 최적화 도구에서 제한을 발견했거나 알려진 것을 발견했을 수도 있고 쿼리가 의미 적으로 동등하지 않을 수도 있습니다. 또는 최적화를 방해하는 요소가 포함되어 있습니다.

따라서 가장 자연스러운 방식으로 쿼리를 작성하는 것이 좋으며 최적화 프로그램에서 실제로 발생하는 성능 문제를 발견했을 때만 벗어날 수 있습니다. 개인적으로 CTE 순위를 매긴 다음 하위 쿼리를 #temp 테이블이 최후의 수단으로 사용합니다.


4
+1 상당히 주관적인 질문으로 판명되었습니다. 나는 지금까지의 답변이 유익하기 때문에 너무 모호해서 닫히지 않기를 바랍니다. 나는 :-) 질문이 변경 될 때 그것을 좋아하지 않지만 OP에서 질문을 좁히는 데 대한 제안이 있습니까?
whytheq

2
나는이 질문이 훌륭하다고 생각하지만 아직 단일 투표 마감이 없다는 것을 알 수 있지만 답변이 격렬하게 시작되면 아마 종료 될 것입니다. 내 대답에서 제안했듯이 CTE와 하위 쿼리 사이에 큰 차이 가있는 특정 사례 가있는 경우 실제 쿼리 및 실행 계획으로 새로운 질문을 시작하십시오 (그리고 dba.se에 더 적합 할 수 있습니다 ) . 해당 쿼리 에 도움이 되는 답변이 동일한 시나리오의 다른 쿼리에 대한 동일한 답변이 아닐 수도 있습니다.
Aaron Bertrand

질문 바로 아래에 링크 link / edit / close / flag가 있습니다. 질문을 닫기위한 투표가 있으면 질문을 닫기 위해 투표 한 사용자 수를 나타내는 close (n)위치가 n표시됩니다. 링크를 클릭하면 해당 사용자가 선택한 이유가 표시됩니다.
Aaron Bertrand

@whytheq는 Bob Beauchemin의 최근 블로그 게시물 도 참조하십시오 . CTE 대 하위 쿼리를 구체적으로 다루지는 않지만 동일한 종류의 개념이 적용됩니다. 성능상의 이유로 직관적이지 않은 패턴을 선택하는 경우 크랩을 문서화하고 다시 방문하여 발견 한 기발한 부분이 실제로 있는지 확인하십시오. 이전 버전을 보유한 신뢰할 수있는 소스 제어 시스템이없는 한 더 자연스러운 버전의 쿼리를 주석 처리 한 상태로 두는 것이 좋습니다.
Aaron Bertrand

1
위의 고정 링크 : sqlskills.com/blogs/bobb/...
ADJenks

19

#temp는 materalized되고 CTE는 아닙니다.

CTE는 구문 일 뿐이므로 이론 상으로는 하위 쿼리 일뿐입니다. 실행됩니다. #temp가 구체화됩니다. 따라서 여러 번 실행되는 조인에서 값 비싼 CTE가 #temp에서 더 나을 수 있습니다. 반면에 쉬운 평가가 실행되지 않지만 몇 번 실행되면 #temp의 오버 헤드가 가치가 없습니다.

테이블 변수를 좋아하지 않는 SO의 일부 사람들이지만 #temp보다 구체화되고 더 빠르기 때문에 그들을 좋아합니다. 테이블 변수에 비해 #temp를 사용하여 쿼리 최적화 프로그램이 더 나은 경우가 있습니다.

#temp 또는 테이블 변수에서 PK를 생성하는 기능은 쿼리 최적화 프로그램에 CTE보다 많은 정보를 제공합니다 (CTE에서 PK를 선언 할 수 없으므로).


약어의 "TVP"는 무엇입니까? #temp와 비슷한?
whytheq

TVP는 인상적으로 들리기 때문에 일반적인 용어가되었습니다. 간단히 말해서 TVP는 매개 변수로 전달 된 테이블입니다. 테이블 변수를 사용한 사람은 누구나 집에있을 수 있습니다.
WonderWorker

1
경고-TVP에는 실행 계획이 없습니다! 가장 간단한 짧은 조회 목록에 TVP를 사용하지 마십시오. 복잡한 조인, 삽입 또는 업데이트를 수행하면 대규모 최적화 문제가 발생할 수 있습니다. 날 믿어 봐
Heliac

12

CTE 대신 # Temp Table을 사용하는 것이 항상 바람직하다고 생각하는 2 가지 사항은 다음과 같습니다.

  1. CTE에 기본 키를 넣을 수 없으므로 CTE가 액세스하는 데이터는 임시 테이블의 PK 또는 인덱스에 액세스하는 대신 CTE 테이블의 각 인덱스를 통과해야합니다.

  2. CTE에 제약 조건, 인덱스 및 기본 키를 추가 할 수 없기 때문에 버그가 발생하고 데이터가 잘못 될 가능성이 높습니다.


어제

다음은 #table 제약 조건이 CTE의 경우가 아닌 나쁜 데이터를 방지 할 수있는 예입니다.

DECLARE @BadData TABLE ( 
                       ThisID int
                     , ThatID int );
INSERT INTO @BadData
       ( ThisID
       , ThatID
       ) 
VALUES
       ( 1, 1 ),
       ( 1, 2 ),
       ( 2, 2 ),
       ( 1, 1 );

IF OBJECT_ID('tempdb..#This') IS NOT NULL
    DROP TABLE #This;
CREATE TABLE #This ( 
             ThisID int NOT NULL
           , ThatID int NOT NULL
                        UNIQUE(ThisID, ThatID) );
INSERT INTO #This
SELECT * FROM @BadData;
WITH This_CTE
     AS (SELECT *
           FROM @BadData)
     SELECT *
       FROM This_CTE;

3
ALWAYS너무 멀지 만 답변 주셔서 감사합니다. 가독성 측면에서 CTE를 사용하는 것이 좋습니다.
whytheq

3
나는 당신의 두 번째 요점을 전혀 이해하지 못합니다. 내가 보는 방식으로, CTE를 정의하는 쿼리는 임시 테이블에 놓을 제약 조건과 유사하며 전자는 임의로 복잡한 술어를 구성 할 수 있지만 후자는 훨씬 제한적입니다 (예 : CHECK여러 행 / 테이블을 참조하는 제약 조건은 허용되지 않음). CTE가 임시 테이블에 해당하지 않는 버그를 나타내는 예를 게시 할 수 있습니까?
onedaywhen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.