関数がnullケース操作でハングする


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ますか?
マーティンスミス

1
@ypercubeによる改善はありませんCOALESCEISNULL修正しました。
カーミット2013

回答:


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同じ値が、それは同じ等価式にOKを簡素化し、同じ計画を与えますが、時にしている@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.8rowsです。

次の参加は

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

colleagueTime3,249,590(やはり)有用なインデックスのないヒープである行テーブルです。

この見積もりの​​不一致は、使用される結合の選択に影響します。ISNULL計画では、ハッシュは一度だけテーブルをスキャンするジョイン選択します。COALESCE計画では、それはまだ一度だけのテーブルをスキャンし、78回の結果をスプールし、それを再生することができるようにする必要がありますことを、ネストされたループ結合を選択したと推定します。つまり、相関するパラメータは変化しないと推定されます。

ネストされたループの計画が2時間後も続いているという事実から、単一のスキャンに対するこの仮定はcolleagueTime非常に不正確である可能性があります。

2つの結合の間の推定行数がはるかに少ない理由については、テーブルの統計を確認できない限りわかりません。私がテストで見積もった行数をスキューさせた唯一の方法は、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これらのテーブルには日付の値がありません。
カーミット2013

@FreshPrinceOfSO-それは残念です。なぜ2つの推定値にこのような大きな不一致があるのか​​、私にはまだわかりません。テストでは、ビットマップフィルターを実行しましたが、追加の述語を使用しても、カーディナリティの推定値は変更されなかったようです。
マーティン・スミス

@FreshPrinceOfSO- 統計スクリプト化したい場合は試してみて理解することができます。
マーティンスミス

私は2008R2にいます。[ スキーマの選択]に移動すると、dboは表示されません。私が使用しない他のスキーマのみ。
カーミット2013

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は、2つの式のみを処理するために使用する適切な関数のようです。

XMLプランの抜粋

を使用したXMLプランCASE、式2は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>

を使用するXMLプランCASE、式2は日付です:

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>

を使用したXMLプランISNULL、式2は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>

を使用するXMLプランISNULL、式2は日付です:

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データ型です。実行計画を表示できますか?
マーティン・スミス

@MartinSmith これは、結果を返すクエリの計画です。2番目の式がのときは、プランがありませんNULL
カーミット2013

内で式をキャストしてCASEも効果はなく、クエリは依然としてハングします。
カーミット2013

2
なぜ2番目のケースの計画がないのですか?クエリが終了しないからというだけですか?もしそうなら、あなたは推定計画を得ることができますか?異なる式がカーディナリティの見積もりを変更するかどうか疑問に思っており、異なるプランになってしまいます。
マーティンスミス

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])ます。それから出てくると推定行が参加するために28393あるISNULLバージョンが、はるかに下げる79.8ためのCASE計画の後半選択肢に加わる影響バージョン。なぜそのような矛盾があるのか​​分かりません。
マーティンスミス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.