質問
SQL ステートメントで DATEDIFF を使用しています。それを選択していますが、WHERE 句でも使用する必要があります。このステートメントは機能しません...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10
次のようなメッセージが表示されます。 無効な列名「InitialSave」
しかし、このステートメントはうまく機能します...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10
私の中のプログラマーは、これは非効率的である (関数を 2 回呼び出しているようだ) と言っています。
そこで質問が 2 つあります。最初のステートメントが機能しないのはなぜですか?2 番目のステートメントを使用して実行するのは非効率ですか?
解決
あなたは、どこ文でselect文で定義された列にアクセスすることはできません。
あなたはしかし、これを行うことができます。
select InitialSave from
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10
追記として - これは、本質的にそれが最初に定義されていますどこの面でどこステートメントにDATEDIFFを移動します。あなたが使用するんだがあれば、文はインデックスがのように効率的に使用されないように、可能であれば避けるべき原因となる場所の列に関数を使用するDateDiff関数その後、あなたはそれを行うようになってきました!
他のヒント
注記: 最初にこの回答を書いたとき、列の 1 つにインデックスを付けると、他の回答よりもパフォーマンスの高いクエリを作成できると言いました (そして Dan Fuller のことにも言及しました)。しかし、私は100%正しく考えていたわけではありませんでした。実際のところ、計算列またはインデックス付き (マテリアライズド) ビューがなければ、テーブル全体のスキャンは次のようになります。 必須, 、比較される 2 つの日付列は、 同じ テーブル!
私は、以下の情報にはまだ価値があると信じています。つまり、1) 異なるテーブルの列間の比較など、適切な状況でパフォーマンスが向上する可能性、2) SQL 開発者がベスト プラクティスに従い、再構築する習慣を促進することです。彼らの考えは正しい方向に向かっています。
条件を Sargable にする
私が言及しているベスト プラクティスは、次のように、1 つの列を比較演算子の片側に単独で移動することです。
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'
前述したように、これによって単一テーブルのスキャンが回避されるわけではありませんが、次のような状況では大きな違いが生じる可能性があります。
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM
dbo.BeginTime B
INNER JOIN dbo.EndTime E
ON B.BeginTime <= E.EndTime
AND B.BeginTime + '00:00:10' > E.EndTime
EndTime
両方の条件にあり、比較の片側に単独で存在します。と仮定すると、 BeginTime
テーブルの行数ははるかに少なく、 EndTime
テーブルの列にインデックスがあります EndTime
, 、これは、他のものを使用するよりもはるかに優れたパフォーマンスを発揮します。 DateDiff(second, B.BeginTime, E.EndTime)
. 。それは今です 検索可能な, これは、有効な「検索引数」があることを意味します。つまり、エンジンとして スキャン の BeginTime
テーブル、それはできます 求める に EndTime
テーブル。どの列を演算子の片側に単独で配置するかを慎重に選択する必要があります。次のようにして実験してみる価値があります。 BeginTime
代数を実行して、それ自体で切り替えます AND B.BeginTime > E.EndTime - '00:00:10'
DateDiff の精度
それも指摘しておきたいと思います DateDiff
戻らない 経過 時間ではなく、回数を数えます。 境界線 交差した。に電話した場合 DateDiff
秒の戻り値を使用する 1
, 、これは意味するかもしれません 3 ms
経過時間、あるいはそれが意味するもの 1997 ms
!これは基本的に +-1 時間単位の精度です。+- 1/2 時間単位の精度を高めるには、以下のクエリを比較する必要があります。 0
に EndTime - BegTime
:
SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'
これにより、最大丸め誤差は合計 2 秒ではなく、わずか 1 秒になります (実際には、floor() 操作)。減算のみができることに注意してください。 datetime
データ型 -- 減算する date
または time
変換する必要がある値 datetime
または、他の方法を使用してより良い精度を取得します( DateAdd
, DateDiff
おそらく他のジャンク、またはおそらくより高精度の時間単位を使用して除算する可能性があります)。
この原則は、時間、日、月などの大きな単位を数える場合に特に重要です。あ DateDiff
の 1 month
62 日離れている可能性があります (2013 年 7 月 1 日から 2013 年 8 月 31 日を考えてください)。
「機能」させるだけでなく、インデックスを使用する必要があります
インデックス付きの計算列、またはインデックス付きのビューを使用します。それ以外の場合は、テーブル スキャンが行われます。十分な行数が得られると、 痛み 遅いスキャンのせいで!
計算列とインデックス:
ALTER TABLE MyTable ADD
ComputedDate AS DATEDIFF(ss,BegTime, EndTime)
GO
CREATE NONCLUSTERED INDEX IX_MyTable_ComputedDate ON MyTable
(
ComputedDate
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ビューとインデックスを作成します。
CREATE VIEW YourNewView
AS
SELECT
KeyValues
,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
GO
CREATE CLUSTERED INDEX IX_YourNewView
ON YourNewView(InitialSave)
GO
あなたが代わりに列の別名の機能を使用する必要があります - それは、COUNT(*)などと同じであるPITA
。の代替として、あなたは計算カラムを使用することができます。