年、月、およびいくつかの数値列を持つテーブルがあります
Year Month Total
2011 10 100
2011 11 150
2011 12 100
2012 01 50
2012 02 200
SELECT
今、私は2011年11月から2012年2月までの間に列を作りたいと思います。toQueryに範囲を使用させたいことに注意してください。テーブルに日付列があるかのように。
年、月、およびいくつかの数値列を持つテーブルがあります
Year Month Total
2011 10 100
2011 11 150
2011 12 100
2012 01 50
2012 02 200
SELECT
今、私は2011年11月から2012年2月までの間に列を作りたいと思います。toQueryに範囲を使用させたいことに注意してください。テーブルに日付列があるかのように。
テーブルで BETWEEN をそのまま使用する方法を考え出すとうまくいきますが、すべての場合でパフォーマンスが低下します。
日付列にインデックスがあり、パフォーマンスにまったく関心がない場合は、代わりに次のことをお勧めします。
DECLARE
@FromDate date = '20111101',
@ToDate date = '20120201';
SELECT *
FROM dbo.YourTable T
WHERE
(
T.[Year] > Year(@FromDate)
OR (
T.[Year] = Year(@FromDate)
AND T.[Month] >= Month(@FromDate)
)
) AND (
T.[Year] < Year(@ToDate)
OR (
T.[Year] = Year(@ToDate)
AND T.[Month] <= Month(@ToDate)
)
);
ただし、このような構造は非常に扱いにくいため、使用したくないことは理解できます。したがって、少なくとも数値計算を使用し、日付から文字列への変換計算よりも少ない CPU を使用する妥協的なクエリを次に示します (ただし、実際のパフォーマンスの問題である強制スキャンを補うには十分ではありません)。
SELECT *
FROM dbo.YourTable T
WHERE
T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;
にインデックスがある場合は、Year
次のようにクエリを送信することで大きなブーストを得ることができます。これにより、シークする機会があります。
SELECT *
FROM dbo.YourTable T
WHERE
T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202
AND T.[Year] BETWEEN 2011 AND 2012; -- allows use of an index on [Year]
これは、単一の式を使用するという要件に違反しますが、BETWEEN
それほど面倒ではなく、Year
インデックスで非常にうまく機能します。
テーブルを変更することもできます。率直に言って、日付データ型の単一の列ではなく、日付部分に個別の数値を使用するのは良くありません。良くない理由は、あなたが今直面している正確な問題のためです。クエリを実行するのは非常に困難です。
バイト数の節約が重要なデータ ウェアハウジングのシナリオでは、日付を数値 ( など) として保存する状況を想像できますが201111
、それはお勧めしません。最善の解決策は、月と年の数値を分割するのではなく、日付を使用するようにテーブルを変更することです。月全体を表すことを認識して、月の最初の日を保存するだけです。
これらの列の使用方法を変更することはできませんが、テーブルを変更できる場合は、永続化された計算列を追加できます。
ALTER Table dbo.YourTable
ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
PERSISTED;
これで、次のことができます。
SELECT *
FROM dbo.YourTable
WHERE
ActualDate BETWEEN '20111101' AND '20120201';
このPERSISTED
キーワードは、スキャンは取得されますが、INSERT または UPDATE ごとに式が計算されて行に格納されるため、各行で計算を行う必要がないことを意味します。ただし、この列にインデックスを追加すると、シークを取得できます。これにより、パフォーマンスが非常に向上します (ただし、全体として、これは実際の日付列を使用するように変更するほど理想的ではありません。より多くのスペースが必要になり、 INSERT と UPDATE に影響します):
CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);
要約: テーブルをまったく変更できない場合は、何らかの方法で妥協する必要があります。日付が別々の列に分割されて保存されている場合、適切に機能する必要な単純な構文を取得することはできません。
(Year > @FromYear OR Year = @FromYear AND Month >= @FromMonth)
AND (Year < @ToYear OR Year = @ToYear AND Month <= @ToMonth)
あなたの例の表は、年と月ごとに1つのレコードしかないことを示しているようです(実際には月ごとの要約表の場合)。そうだとすれば、数十年の活動を経ても、テーブルに蓄積されるデータはごくわずかである可能性があります。連結式ソリューションは機能し、パフォーマンス(この場合)は問題になりません。
SELECT * FROM Table WHERE ((Year * 100) + Month) BETWEEN 201111 AND 201202
そうではなく、テーブルに実際に多数のレコード(数千を超えるレコード)がある場合は、いくつかの選択肢があります。
YYYYMMの形式(整数値またはテキスト)で年と月を格納するようにテーブルを変更します。この列は、現在の年とインデックスの列を置き換えることも、それらに追加することもできます(ただし、これは通常の形式ではありません)。この列にインデックスを付け、それに対してクエリを実行します。
上記のように、年と月に1つのレコードと、インデックス付け可能な列を含む別のテーブルを作成します。クエリでは、このテーブルを結合してソーステーブルに戻し、小さいテーブルのインデックス付き列に対してクエリを実行します。