これまでのキャストは検索可能ですが、良いアイデアですか?


47

SQL Server 2008では、日付データ型が追加されました。

datetime列のキャストdate検索可能であり、datetime列のインデックスを使用できます。

select *
from T
where cast(DateTimeCol as date) = '20130101';

他のオプションは、代わりに範囲を使用することです。

select *
from T
where DateTimeCol >= '20130101' and
      DateTimeCol < '20130102'

これらのクエリは同等に優れていますか?


4
実行計画には何と書かれていますか?
a_horse_with_no_name

3
where cast(date_column as date) = 'value'似たC#が表示されると、LINQ2SQLがSQLを生成することに気づかずにはいられませんwhere obj.date_column.Date == date_variable
GSerg

6
これは優れたConnectアイテムです。:)
ロブファーリー

1
Connectサイトは、ウィキペディアで検索引数だけでなく、として削除されました
Ivanzinho

回答:


59

これまでのキャストの可算性の背後にあるメカニズムは、動的シークと呼ばれます。

SQL Serverは内部関数GetRangeThroughConvertを呼び出して、範囲の開始と終了を取得します。

多少驚くべきことに、これはリテラル値と同じ範囲ではありません

1ページあたり1行、1日あたり1440行のテーブルを作成する

CREATE TABLE T
  (
     DateTimeCol DATETIME PRIMARY KEY,
     Filler      CHAR(8000) DEFAULT 'X'
  );

WITH Nums(Num)
     AS (SELECT number
         FROM   spt_values
         WHERE  type = 'P'
                AND number BETWEEN 1 AND 1440),
     Dates(Date)
     AS (SELECT {d '2012-12-30'} UNION ALL
         SELECT {d '2012-12-31'} UNION ALL
         SELECT {d '2013-01-01'} UNION ALL
         SELECT {d '2013-01-02'} UNION ALL
         SELECT {d '2013-01-03'})
INSERT INTO T
            (DateTimeCol)
SELECT DISTINCT DATEADD(MINUTE, Num, Date)
FROM   Nums,
       Dates 

その後、実行中

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT *
FROM   T
WHERE  DateTimeCol >= '20130101'
       AND DateTimeCol < '20130102'

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'; 

最初のクエリには1443読み取りがあり、2番目のクエリには1 2883日全体を読み取り、残りの述部に対してそれを破棄しています。

プランは、シーク述語が

Seek Keys[1]: Start: DateTimeCol > Scalar Operator([Expr1006]), 
               End: DateTimeCol < Scalar Operator([Expr1007])

その>= '20130101' ... < '20130102'ため、読み取りの代わりに> '20121231' ... < '20130102'すべての2012-12-31行を破棄します。

これに依存する別の欠点は、カーディナリティの推定が従来の範囲クエリほど正確ではない可能性があることです。これは、SQL Fiddleの修正バージョンで確認できます

テーブル内の100行すべてが述部と一致するようになりました(同じ日にすべての日付時刻が1分離れている)。

2番目の(範囲)クエリは、100が一致することを正しく推定し、クラスター化インデックススキャンを使用します。CAST( AS DATE)クエリが間違って一列のみが一致することを推定し、キーを検索して計画を作成します。

統計は完全に無視されません。テーブル内のすべての行が同じdatetimeで、述部(20130101 00:00:00または20130101 01:00:00)に一致する場合、プランには、推定31.6228行のクラスター化インデックススキャンが表示されます。

100 ^ 0.75 = 31.6228

したがって、その場合、推定値はここの式から導出されます

テーブル内のすべての行が同じでdatetimeあり、述語と一致しない場合(例:)20130102 01:00:00、推定行カウント1およびルックアップ付きプランにフォールバックします。

テーブルに複数のDISTINCT値がある場合、推定される行は、クエリが正確に探していた場合と同じように見えます20130101 00:00:00

統計ヒストグラムにステップがある2013-01-01 00:00:00.000場合、推定値はEQ_ROWS(つまり、その日付の他の時間を考慮に入れていない)に基づきます。それ以外の場合、ステップがない場合はAVG_RANGE_ROWS、周囲のステップのを使用するかのように見えます。

datetime多くのシステムでは、約3ミリ秒の精度を持っているが、非常に少数の実際の重複した値となり、この数は1になります。


1
こんにちは、マーティン、TL;DRいくつかの箇条書きを付けたパーツを追加し、その場合、最新のキャストが良いアイデアかどうかを追加できますか?
TT。

6
@TT。ポイントは、それは良い考えではないということだと思います。なぜチートシートを必要とする方法を使用したいのですか?
アーロンバートランド

10

これには、Martinの長年のGreatAnswer®があることがわかっていますが、新しいバージョンのSQL Serverでの動作にいくつかの変更を加えたいと思いました。これは、2008R2までテストされただけのようです。

いくつかのカーディナリティの推定時間移動を可能にする新しいUSEヒント使用すると、状況がいつ変化したかを確認できます。

SQL Fiddleと同じセットアップを使用します。

CREATE TABLE T ( ID INT IDENTITY PRIMARY KEY, DateTimeCol DATETIME, Filler CHAR(8000) NULL );

CREATE INDEX IX_T_DateTimeCol ON T ( DateTimeCol );


WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
     E02(N) AS (SELECT 1 FROM E00 a, E00 b),
     E04(N) AS (SELECT 1 FROM E02 a, E02 b),
     E08(N) AS (SELECT 1 FROM E04 a, E04 b),
     Num(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY E08.N) FROM E08)
INSERT INTO T(DateTimeCol)
SELECT TOP 100 DATEADD(MINUTE, Num.N, '20130101')
FROM Num;

次のようにさまざまなレベルをテストできます。

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_100' ));
GO

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_110' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_120' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_130' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_140' ));
GO 

これらのすべての計画はここで利用可能です。互換性レベル100と110はどちらもキールックアッププランを提供しますが、互換性レベル120から始めて、100行の推定で同じスキャンプランを取得し始めます。これは、互換レベル140まで当てはまります。

ナッツ

ナッツ

ナッツ

>= '20130101', < '20130102'計画のカーディナリティの推定値は100のままであり、予想されていました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.