既存の(機能する)回答にはすべて、次の2つの問題のいずれかがあります。
- 検索対象の列のインデックスは無視されます
- 意図されていないデータを(潜在的に)選択し、結果を黙って破壊します。
1。無視されるインデックス:
ほとんどの場合、検索対象の列に関数が呼び出されている場合(CAST
のように、暗黙的に含まれます) )、オプティマイザは列のインデックスを無視し、すべてのレコードを検索する必要があります。簡単な例を次に示します。
私たちはタイムスタンプを扱っており、ほとんどのRDBMSは、この情報をある種の増加する値、通常はlong
として保存する傾向があります。 またはBIGINTEGER
ミリ秒/ナノ秒のカウント。したがって、現在の時刻は次のように表示/保存されます:
1402401635000000 -- 2014-06-10 12:00:35.000000 GMT
'Year'値が表示されません('2014'
)そこにいますか?実際、前後に翻訳するためのかなり複雑な数学があります。したがって、検索された列で抽出/日付部分関数のいずれかを呼び出す場合、サーバーは、結果にそれを含めることができるかどうかを判断するために、すべての計算を実行する必要があります。小さなテーブルでは、これは問題ではありませんが、選択された行の割合が減少するにつれて、これはますます大きなドレインになります。次に、この場合、MONTH
について質問するためにもう一度実行します。 ...まあ、あなたは写真を手に入れます。
2。意図しないデータ:
BETWEEN
を使用して、SQLServerの特定のバージョンと列のデータ型に応じて (または同様の包括的上限範囲:<=
)誤ったデータが選択される可能性があります。基本的に、「翌」日の深夜のデータを含めたり、「現在の」日の記録の一部を除外したりする可能性があります。
すべきこと やっている:
したがって、データにとって安全で、インデックスを使用する方法が必要です(実行可能な場合)。正しい方法は次の形式です:
WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth
1か月しかないことを考えると、@startOfPreviousMonth
簡単に置き換える/派生させることができます:
DATEADD(month, -1, @startOCurrentfMonth)
サーバーで現在の月の開始を取得する必要がある場合は、次の方法で取得できます。
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
ここに簡単な説明があります。最初のDATEDIFF(...)
現在の時代の始まりとの違いを取得します(0001-01-01
--AD、CE、その他)、基本的に大きな整数を返します。これは、現在の開始までの月数です。 月。次に、この数値を、指定された月の初めである時代の始まりに追加します。
したがって、完全なスクリプトは次のようになる可能性があります。
DECLARE @startOfCurrentMonth DATETIME
SET @startOfCurrentMonth = DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
SELECT *
FROM Member
WHERE date_created >= DATEADD(month, -1, @startOfCurrentMonth) -- this was originally misspelled
AND date_created < @startOfCurrentMonth
したがって、すべての日付操作は、1つの値に対して1回だけ実行されます。オプティマイザーはインデックスを自由に使用でき、誤ったデータが含まれることはありません。