6

SQL ステートメントで DATEDIFF を使用しています。私はそれを選択していますが、WHERE 句でも使用する必要があります。この発言は通用しない…

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10

次のメッセージが表示されます: Invalid column name "InitialSave"

しかし、このステートメントはうまく機能します...

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10

私のプログラマーは、これは非効率的だと言っています (関数を 2 回呼び出しているようです)。

そこで2つ質問です。最初のステートメントが機能しないのはなぜですか? 2番目のステートメントを使用してそれを行うのは非効率的ですか?

4

5 に答える 5

7

注:最初にこの回答を書いたとき、列の1つにインデックスを付けると、他の回答よりもパフォーマンスの高いクエリを作成できると言いました(そしてDan Fullerのことを述べました)。しかし、100%正しく考えていたわけではありません。実際には、計算列またはインデックス付き (マテリアライズド) ビューがなければ、比較対象の 2 つの日付列が同じテーブルのものであるため、完全なテーブル スキャンが必要になります。

以下の情報にはまだ価値があると思います。つまり、1) 異なるテーブルの列を比較する場合など、適切な状況でパフォーマンスが向上する可能性、および 2) SQL 開発者にベスト プラクティスに従って再形成する習慣を促進することです。彼らの考えを正しい方向に。

条件を検索可能にする

私が言及しているベスト プラクティスは、次のように、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できます。演算子の片側にある列を慎重に選択する必要があります。代数を実行して切り替えることで、単独で配置することで実験する価値があります。EndTimeBeginTimeAND B.BeginTime > E.EndTime - '00:00:10'

DateDiff の精度

は経過DateDiff時間を返すのではなく、通過した境界の数をカウントすることも指摘しておく必要があります。using secondsの呼び出しで が返された場合、これは経過時間または!を意味する可能性があります。これは基本的に、時間単位 +-1 の精度です。+- 1/2 時間単位の精度を高めるには、次のクエリと比較する必要があります。DateDiff13 ms1997 ms0EndTime - 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は62 日離れている1 month可能性があります (2013 年 7 月 1 日から 2013 年 8 月 31 日までと考えてください)。

于 2009-05-15T18:52:54.503 に答える
5

where ステートメントの select ステートメントで定義された列には、where が実行されるまで生成されないため、アクセスできません。

ただし、これを行うことができます

select InitialSave from 
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10

補足として、これは基本的に、最初に定義された場所に関して、DATEDIFF を where ステートメントに移動します。where ステートメントの列で関数を使用すると、インデックスが効率的に使用されないため、可能であれば避ける必要がありますが、datediff を使用する必要がある場合は、それを行う必要があります。

于 2009-05-15T18:43:58.517 に答える
3

それを「機能させる」だけでなく、インデックスを使用する必要があります

インデックス付きの計算列、またはインデックス付きのビューを使用します。そうしないと、テーブルスキャンが実行されます。十分な行を取得すると、低速スキャンの痛みを感じるでしょう!

計算された列とインデックス:

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
于 2009-05-15T19:09:36.617 に答える
2

列エイリアスの代わりに関数を使用する必要があります-count(*)などと同じです.PITA。

于 2009-05-15T18:40:42.630 に答える
1

代わりに、計算列を使用できます。

于 2009-05-15T18:42:19.067 に答える