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

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

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

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

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

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

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


편집하다

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



답변

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

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

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

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

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

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


답변

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

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

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

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


답변

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

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

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

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


답변

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;


답변