널 케이스 조작으로 함수가 정지 함


9

시작 날짜와 종료 날짜를 허용하는 함수를 만들었습니다. 종료 날짜는 선택 사항입니다. 그런 다음 CASE종료 날짜가 없으면 시작 날짜를 사용하도록 필터 에을 작성했습니다 .

CASE WHEN @dateEnd IS NULL
    THEN @dateStart
    ELSE @dateEnd
END

가장 최근 달의 데이터에 대한 함수를 호출하면 :

SELECT * FROM theFunction ('2013-06-01', NULL)

... 쿼리가 중단됩니다. 종료일을 지정하면 :

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')

... 결과가 정상적으로 반환됩니다. 함수에서 코드를 가져 와서 쿼리 창에서 제대로 실행했습니다. 바이올린 문제도 복제 할 수 없습니다. 다음과 같은 쿼리 :

SELECT * FROM theFunction ('2013-04-01', '2013-06-01')

... 잘 작동합니다.

NULL종료 날짜 동안 a 가 전달 될 때 함수가 중단 될 수있는 쿼리 (아래)가 있습니까?

SQL 바이올린

  • 실행 계획 에 대한SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
  • 예상 계획 에 대한SELECT * FROM theFunction ('2013-06-01', NULL)

더 많은 논리를 게시 할 수 있습니까? 당신이 가지고있는 것은 문제를 일으키지 않아야합니다.
케네스 피셔

3
사용자가 교체 할 경우 CASECOALESCE(@dateEnd,@dateStart), 문제는 여전히 나타나지 않습니다?
ypercubeᵀᴹ

2
그리고 ISNULL()?
ypercubeᵀᴹ

3
바쁘거나 무언가를 기다리고 있습니까? "걸려"있는 동안 무엇을 SELECT task_state FROM sys.dm_os_tasks WHERE session_id = x 보여줍니까? RUNNING상태가 아닌 많은 시간을 소비하면 해당 세션이 대기하는 유형은 무엇 sys.dm_os_waiting_tasks입니까?
Martin Smith

1
@ypercube로 개선되지 않았습니다 COALESCE. ISNULL고쳤다.
커밋

답변:


7

초기 쿼리의 일부는 다음과 같습니다.

  FROM   [dbo].[calendar] a
          LEFT JOIN [dbo].[colleagueList] b
            ON b.[Date] = a.d
   WHERE  DAY(a.[d]) = 1
          AND a.[d] BETWEEN @dateStart AND COALESCE(@dateEnd,@dateStart) 

계획의 해당 섹션은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

수정 된 쿼리 BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)에 동일한 조인에 대해

여기에 이미지 설명을 입력하십시오

차이점은 ISNULL더 단순화하고 결과적으로 다음 조인에 더 정확한 카디널리티 통계를 얻는 것입니다. 이것은 인라인 테이블 값 함수이며 리터럴 값으로 호출하여 비슷한 작업을 수행 할 수 있습니다.

 a.[d] BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart) 
 a.[d] BETWEEN '2013-06-01' AND ISNULL(NULL,'2013-06-01') 
 a.[d] BETWEEN '2013-06-01' AND '2013-06-01'
 a.[d] = '2013-06-01'

그리고 동등 조인 술어가 b.[Date] = a.d있으므로 계획은 또한 동등 술어를 보여줍니다 b.[Date] = '2013-06-01'. 결과적으로 28,393행 의 카디널리티 추정은 매우 정확할 것입니다.

를 들어 CASE/의 COALESCE버전 때 @dateStart@dateEnd같은 값 같은 평등의 표현으로 그것을 단순화 확인하고 같은 계획을 제공하지만 때 @dateStart = '2013-06-01'@dateEnd IS NULL단지까지로 간다

a.[d]>='2013-06-01' AND a.[Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END

또한에 대한 암시 적 술어로도 적용됩니다 ColleagueList. 이번에 예상되는 행 수는 79.8행입니다.

다음 조인은

   LEFT JOIN colleagueTime
     ON colleagueTime.TC_DATE = colleagueList.Date
        AND colleagueTime.ASSOC_ID = CAST(colleagueList.ID AS VARCHAR(10)) 

colleagueTimeA는 3,249,590(다시) 인 행 테이블없이 유용한 인덱스 분명히 힙.

이 추정값의 불일치는 사용 된 조인 선택에 영향을줍니다. ISNULL계획은 해시 한 번만 테이블을 스캔 조인을 선택합니다. COALESCE계획은 여전히 한 번만 테이블을 스캔하고 78 번 결과를 스풀하고 재생할 수 있도록해야한다는 중첩 루프 조인을 선택하고 추정하고있다. 즉, 상관 파라미터가 변하지 않을 것으로 추정한다.

중첩 루프 계획이 여전히 2 시간 후에 진행되었다는 사실 때문에 단일 스캔에 대한 이러한 가정은 colleagueTime매우 부정확 한 것으로 보입니다.

두 조인 사이의 예상 행 수가 너무 적은 이유에 대해서는 테이블의 통계를 볼 수 없으면 확실하지 않습니다. 테스트에서 많은 행을 추가하는 것이 예상 행 수를 왜곡시키는 유일한 방법은 NULL(실제로 반환 된 행 수가 동일하게 유지되었지만 예상 행 수를 줄였습니다).

COALESCE테스트 데이터가 있는 계획 의 예상 행 수 는

number of rows matching >= condition * 30% * (proportion of rows in the table not null)

또는 SQL에서

SELECT 1E0 * COUNT([Date]) / COUNT(*) * ( COUNT(CASE
                                                  WHEN [Date] >= '2013-06-01' THEN 1
                                                END) * 0.30 )
FROM   [dbo].[colleagueList] 

그러나 이것은 열에 NULL값 이 없다는 의견으로 제곱되지 않습니다 .


"해당 테이블의 날짜 열에 NULL 값의 비율이 매우 높습니까?" NULL해당 테이블에 날짜 값 이 없습니다 .
커밋

@FreshPrinceOfSO-유감입니다. 나는 그때 두 추정치에 왜 그렇게 큰 불일치가 있는지에 대해 전혀 모른다. 테스트에서 비트 맵 필터를 수행했으며 추가 술어가 카디널리티 예상치를 변경하지 않는 것 같습니다.
Martin Smith

@FreshPrinceOfSO- 통계를 스크립팅하는 것처럼 느껴지면 시도해 볼 수 있습니다.
Martin Smith

저는 2008R2에 있습니다. 내가 얻을 때 스키마를 선택 , dbo표시되지 않습니다. 내가 사용하지 않는 다른 스키마 만.
커밋

4

데이터 형식에 문제가있는 것 같습니다. ISNULL이 문제를 해결했습니다 ( ypercube 감사 ). 몇 가지 연구를 한 후에, 내가 사용 하고있는 진술 COALESCE과 같습니다CASE .

CASE
   WHEN (expression1 IS NOT NULL) THEN expression1
   WHEN (expression2 IS NOT NULL) THEN expression2
   ...
   ELSE expressionN
END

폴 화이트는 다음 과 같이 설명 합니다.

COALESCE( expression [ ,...n ] ) 데이터 형식 우선 순위가 가장 높은 식의 데이터 형식을 반환합니다.

ISNULL(check_expression, replacement_value) check_expression과 동일한 유형을 반환합니다.

데이터 유형 문제를 피하기 위해 ISNULL두 개의 표현식 만 처리하는 데 사용하는 것이 적합한 것으로 보입니다 .

XML 계획 발췌

CASE식 2를 사용하는 XML 계획NULL다음과 같습니다.

SELECT * FROM theFunction ('2013-06-01', NULL)
<ScalarOperator ScalarString="CASE WHEN (1) THEN '2013-06-01' ELSE NULL END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Const ConstValue="(1)"/>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="'2013-06-01'"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

CASE, 식 2를 사용한 XML 계획 은 날짜입니다.

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
      </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

ISNULL식 2를 사용하는 XML 계획NULL다음과 같습니다.

SELECT * FROM theFunction ('2013-06-01', NULL)
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

ISNULL, 식 2를 사용한 XML 계획 은 날짜입니다.

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

그러나 그것이 왜 제대로 작동했는지 설명하지는 않습니다 SELECT * FROM theFunction ('2013-06-01', '2013-06-01'). 표현식 데이터 유형은 여전히 ​​동일합니다. 그리고 두 매개 변수는 모두 date데이터 유형입니다. 실행 계획을 볼 수 있습니까?
Martin Smith

@MartinSmith 다음 은 결과를 반환하는 쿼리 계획 입니다. 두 번째 표현이이면 계획이 없습니다 NULL.
커밋

내부에 식을 캐스팅 CASE해도 아무런 효과가 없었으며 쿼리는 여전히 중단됩니다.
커밋

2
두 번째 경우에 대한 계획은 어떻습니까? 쿼리가 끝나지 않았기 때문입니까? 그렇다면 예상 요금제를받을 수 있습니까? 다른 표현이 카디널리티 예상치를 변경하고 다른 계획으로 끝날지 궁금합니다.
Martin Smith

3
ISNULL이 같은 계획의 외모가 더 간단 해집니다. ColleagueList에 대한 동등한 동등 술어가있는 [Date]='2013-06-01'반면, CASE에 대한 술어가 [Date]>='2013-06-01' AND [Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END AND PROBE([Bitmap1067],[Date])있습니다. 해당 조인에서 나오는 예상 행은 ISNULL버전의 경우 28,393 이지만 버전의 경우 조망 선택에 영향 79.8을주는 CASE버전의 경우 훨씬 낮습니다 . 왜 그러한 불일치가 있는지 확실하지 않습니다.
Martin Smith
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.