2010-07-08 7 views
3

SQL 전문가에게 질문이 있습니다.SQL Server 2008 실행 계획 질문

거의 동일한 구조의 테이블이 두 개 있습니다.

저장 프로 시저에 전달 된 매개 변수에 따라 하나 또는 다른 테이블에서 데이터를 수집해야합니다.

가장 좋은 방법은?

이러한 테이블을 하나의 테이블로 결합하는 것은 바람직하지 않습니다.

나는 (MS SQL 서버 2008) 다음 그것을했다 :

Select * 
FROM 
    String s 
    JOIN (
     SELECT id 
      ,TypeCode 
      ,ProdNo   
       FROM Table1 
     WHERE @param = 1 AND TypeCode= 'INV' 

     UNION 

     SELECT id 
      ,TypeCode 
      ,ProdNo 
     FROM Table2 
     WHERE @param = 2 AND TypeCode= 'INV' 

    ) m ON m.Id = s.Id 
WHERE s.Id = 256 

을하지만 실행 계획을보고 할 때 병렬 스레드 만 @param에 의해 필터링 그 이후 두 테이블에서 데이터를 얻었 기 때문에 놀랐습니다 값.

필자는 필터링이 첫 번째 단계에서 이루어지며 단일 테이블에서 수집 된 데이터라고 생각했습니다.

쿼리를 두 개의 쿼리로 분할하지 않고 IF 연산자 만 사용하여 한 테이블에서만 선택하는 방법이 있습니까?

감사합니다.

+0

전체 저장 프로 시저를 표시 할 수 있습니까? 계획은 다른 요인들로 인해 차선책 일 수 있습니다. 예 : 매개 변수 스니핑, 합집합 대신에 합집합 등이 있습니다. –

+0

이 글은 Erland Sommarskog가 작성한 T-SQL의 동적 검색 조건 (http://www.sommarskog.se/dyn-search.html)을 반드시 읽어야합니다. 반복 코드에 대해 걱정할 필요가 없습니다. 이는 숙제가 아닙니다. 실행 계획에 인덱스를 사용하는 것에 대해 걱정하십시오. SQL 코드를 "pretty"로 만들 때 고려해야 할 유일한 것은 indenting & case입니다. 다른 변경으로 인해 쿼리 계획이 느려질 수 있습니다. 나는 슈퍼 슬로우 쿼리의 초고속 쿼리 결과에 사소한 변화를 보았다. GO SPEED (색인 사용) 및 필요에 따라 코드를 복제하십시오. 또한보십시오 : http://www.sommarskog.se/dynamic_sql.html –

+0

2KM - 그렇습니다, 나는 당신에 동의한다. Index Seek로 실행 된 쿼리. 사용하기에 충분히 빠릅니다. 그 중 하나가 완전히 불필요한 경우에 두 하위 쿼리를 실행하지 않는 방법에 흥미 롭습니다. – Cheburek

답변

4

정말 이것을 읽을 필요가 있습니다. Dynamic Search Conditions in T-SQL by Erland Sommarskog. 반복 코드에 대해 걱정할 필요가 없습니다. 이는 숙제가 아닙니다. 실행 계획에 인덱스를 사용하는 것에 대해 걱정하십시오. SQL 코드를 "pretty"로 만들 때 & 경우 들여 쓰기 만 고려하면 다른 변경으로 인해 쿼리 계획의 속도가 느려질 수 있습니다. 나는 슈퍼 슬로우 쿼리의 초고속 쿼리 결과에 사소한 변화를 보았다. GO SPEED (색인 사용) 및 필요에 따라 코드를 복제하십시오. 또한 참조 : The Curse and Blessings of Dynamic SQL

SQL 2008 SP1 CU5 (10.0.2746) 및 SQL 2008 R2 CU1 (10.50.1702) 이상을 실행하는 경우 질문이 sql-server- 모든 버전의 SQL 2008 또는 2005에 나타나지 않는 OPTION(RECOMPILE)의 "동적 검색 조건"문서에서 설명한대로 동작합니다.이 동작은 기본적으로 런타임시 @Local_Variables 값을 평가하고 이에 따라 쿼리를 다시 컴파일합니다. 귀하의 경우, 이로 인해 UNION의 절반이 컴파일 될 때 제거 될 것입니다.

+0

그것은 정말로 "반드시 읽어야 만하는"기사입니다. 매우 도움이됩니다. – Cheburek

4

간단한 IF 문을 사용할 수 있습니까?

IF @Param = 1 
    BEGIN 
    EXEC SQL 
    END 
ELSE IF @Param = 2 
    BEGIN 
    EXEC SQL 
    END 
ELSE 
    RAISERROR('Invalid Parameter', 16, 1) 

또는 동적으로 쿼리를 작성하고 sp_executesql 저장 프로 시저를 사용하여 실행할 수 있습니다.

DECLARE @Sql NVARCHAR(100) 

SET @Sql = N'SELECT * FROM (' 

IF @Param = 1 
    SET @Sql = @Sql + N'SELECT 1 a, 2 b, 3 c' 
ELSE IF @param = 2 
    SET @Sql = @Sql + N'SELECT 4 a, 5 b, 6 c' 
ELSE 
    RAISERROR('Invalid Parameter', 16, 1) 

SET @Sql = @Sql + ') tbl' 
EXEC sp_executesql @sql 
+0

물론 가능합니다. 게시물의 끝 부분에서 지적했습니다. 그러나 1) 추출 할 수 없게됩니다. 보기 2) 나는 이미 동일한 코드의 두 부분을 가지고있다. 내가 4 개의 거대한 선택이있을 것보다 그것을 다시 나누면 - 그런 코드를 지원하는 것은 재미 있지 않다. 그래서 나는 그것을 단일 쿼리 내에서 만들 수있는 다른 방법이 없다면 사용할 것이다. 어쨌든 고맙습니다. – Cheburek

+0

동일한 코드를 두 번 사용하지 않도록 다른 방법을 추가했습니다. HTH –

+1

동적 SQL ... 가능한 곳에서는 사용하지 않으려 고합니다. 나는 당신이 왜 그런지 알고 있다고 생각한다.) IMHO 정적 코드 블록 두 개가있는 것이 더 좋다. 물론 다른 방법이다. 그것을 가져 주셔서 감사합니다! – Cheburek

0

SQL 서버는 영리 아니다 - 쿼리를 작성할 때 당신은 단지 당신이 (불필요한 문을 보내지 않고) 원하는 데이터를 얻기 위해 SQL의 최소 금액을 보낼 수 있도록, 또한 (정보의 대부분의 금액을 제공해야 가능한 경우 쿼리 최적화 프로그램에 데이터에 대한 힌트를 제공 할 수 있습니다. 보시다시피, 귀하가 보낸 모든 SQL이 실행됩니다.

그래서 내가 읽고있는 것으로부터 동적 SQL을 사용해야하는 것 같습니다. 이것은 또한 SQL의 공통 부분을 복제 양에 맞게 병합 할 수있는 이점을 제공합니다. 예를 들어, 당신은 (당신의 내부 코드를 복용 - 당신이 주위에 물건의 나머지 부분을 포장 할 수 있습니다) 수 : 좀 더 복잡한 무언가로이 바람 거라면

DECLARE @sql NVARCHAR(1000) 

SET @sql = 'SELECT id, TypeCode, ProdCode' 
IF @param = 1 
    SET @sql = @sql + ' FROM table1' 
IF @param = 2 
    SET @sql = @sql + ' FROM table2' 
SET @sql = @sql + ' WHERE TypeCode = ''INV''' 

EXECUTE sp_ExecuteSQL @sql 

그냥,주의, 작은 바비 테이블에 대해서 : sp_ExecuteSQL을 악용하여 구멍을 열 수는 있지만 올바르게 매개 변수화 된 동적 SQL을 사용하면 올바르게 저장 프로 시저만큼 사용됩니다.

1

ID 필터를 결합체 안에 넣는 것이 좋습니다. UNIONUNION ALL으로 변경했습니다. 이렇게하면 tDUMMY 테이블 ( 하나의 더미 행이 )이 탄 줄을 만들 수있는 경우 DISTINCT 행

Select * 
FROM 
    String s 
    JOIN ( 
     SELECT id 
      ,TypeCode 
      ,ProdNo   
       FROM Table1 
     WHERE @param = 1 AND TypeCode= 'INV' AND id = 256 

     UNION ALL 

     SELECT id 
      ,TypeCode 
      ,ProdNo 
     FROM Table2 
     WHERE @param = 2 AND TypeCode= 'INV' AND id = 256 

    ) m ON m.Id = s.Id 
WHERE s.Id = 256 
+0

나는 UNION ALL에 관해서는 당신과 동의하지만 ID에 대해서는 동의하지 않습니다. 첫 번째 단계의 실행 계획에서 볼 수 있듯이 SQL Server는 outer select에 명시 적으로 설정된 경우 하위 쿼리에 id 제한을 자동으로 추가하는 것으로 충분합니다. – Cheburek

+0

그렇습니다. UNION보다 UNION보다 빠릅니다. select 중 하나라도 항상 비어 있습니다. 고맙습니다. 매우 유용했습니다. – Cheburek

0

평가를 피할 수 있습니다.

Select 
    * 
FROM 
    String s 
    JOIN ( 
     SELECT 
      id, TypeCode, ProdNo 
     FROM 
      tDUMMY INNER JOIN Table1 ON TypeCode= 'INV' 
     WHERE 
      @param = 1 

UNION ALL 

     SELECT 
      id, TypeCode, ProdNo 
     FROM 
      tDUMMY INNER JOIN Table2 ON TypeCode= 'INV' 
     WHERE 
      @param = 2 
    ) m ON m.Id = s.Id 
WHERE s.Id = 256 

이론적으로 쿼리 최적화 프로그램은 먼저 tDUMMY 테이블을 필터링해야하고 조인을 시도합니다. 따라서 @param = 1이면 두 번째 쿼리가 훨씬 빨리 빠져 나옵니다 (다시 한 번 tDUMMY 행을 확인하지만 체크 테이블 2가 아님)

주 - 또한 UNION ALL 어쨌든 한쪽에는 항상 아무런 행도 반환하지 않기 때문에 많은 영향을 미칩니다.

+0

그냥 sidenote입니다. 충분한 권한이있는 SQL Server에 도착하면 분명히 이것을 시도 할 것입니다. 현재 내가 접근 할 수있는 유일한 곳은 메모장입니다 :-). 한편으로는 어떤 이유로 든 소리가 나지 않으면 이것을 쏴 버릴 수 있습니다. – potatopeelings

+0

테이블을 만들고 쿼리를 테스트했습니다. 흥미로운 아이디어이지만 결과가 좋지 않습니다. 두 테이블 (tDummy와 Table1 또는 Table2)에서 실행 선택이 동시에 실행되고 tDummy 필터가 @param과 함께 실행되면 결과가 결합됩니다. 따라서 어떤 이점도 없습니다. 이 쿼리는 tDummy 테이블이없는 쿼리가 두 개 모두 하나의 창에서 실행되는 경우에만 40 %의 데이터 만 가져옵니다. 하지만 아이디어는 실제로 매우 흥미 롭습니다. 감사합니다 ... – Cheburek

+0

감사합니다. 옵티마이 저는 아마도 @param 절을 나머지 술어로 사용했습니다 .-(내가 생각할 수있는 유일한 다른 점은 tDUMMY를 하위 쿼리로 만들고 해시 조인을 강제하는 것입니다. SELECT id, TypeCode, ProdNo FROM (SELECT * from tDUMMY WHERE @param = 1) Inner JOIN Table1 ON TypeCode = 'INV'OPTION (HASH JOIN).하지만 여기서 추측하고 있습니다. (내가 완전히 알고 있다고 말할 때 작동하지 않는 한 :-)) – potatopeelings