4

年、月、およびいくつかの数値列を持つテーブルがあります

Year   Month  Total
2011     10    100
2011     11    150
2011     12    100  
2012     01    50
2012     02    200

SELECT今、私は2011年11月から2012年2月までの間に列を作りたいと思います。toQueryに範囲を使用させたいことに注意してください。テーブルに日付列があるかのように。

4

3 に答える 3

7

テーブルで BETWEEN をそのまま使用する方法を考え出すとうまくいきますが、すべての場合でパフォーマンスが低下します。

  • せいぜい、行を日付として処理するのではなく、行に対して何らかの計算を行うために、より多くの CPU を消費します。
  • 最悪の場合、テーブル内のすべての行でテーブル スキャンが強制されますが、列にインデックスがある場合は、適切なクエリを使用してシークが可能です。制約を 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);

要約: テーブルをまったく変更できない場合は、何らかの方法で妥協する必要があります。日付が別々の列に分割されて保存されている場合、適切に機能する必要な単純な構文を取得することはできません。

于 2012-12-20T01:56:27.127 に答える
2
(Year > @FromYear OR Year = @FromYear AND Month >= @FromMonth)
AND (Year < @ToYear OR Year = @ToYear AND Month <= @ToMonth)
于 2012-12-20T02:24:31.370 に答える
1

あなたの例の表は、年と月ごとに1つのレコードしかないことを示しているようです(実際には月ごとの要約表の場合)。そうだとすれば、数十年の活動を経ても、テーブルに蓄積されるデータはごくわずかである可能性があります。連結式ソリューションは機能し、パフォーマンス(この場合)は問題になりません。

SELECT * FROM Table WHERE ((Year * 100) + Month) BETWEEN 201111 AND 201202

そうではなく、テーブルに実際に多数のレコード(数千を超えるレコード)がある場合は、いくつかの選択肢があります。

  1. YYYYMMの形式(整数値またはテキスト)で年と月を格納するようにテーブルを変更します。この列は、現在の年とインデックスの列を置き換えることも、それらに追加することもできます(ただし、これは通常の形式ではありません)。この列にインデックスを付け、それに対してクエリを実行します。

  2. 上記のように、年と月に1つのレコードと、インデックス付け可能な列を含む別のテーブルを作成します。クエリでは、このテーブルを結合してソーステーブルに戻し、小さいテーブルのインデックス付き列に対してクエリを実行します。

于 2012-12-20T02:33:58.363 に答える