2016-03-21 19 views
4

利用可能なすべての日付(終わりの時系列)のレコードの最新バージョンのテーブルをクエリする必要があります。以下の例は私が達成しようとしていることを示しています。SQLを使用して最新バージョンのレコードを効率的にクエリする

私の質問は、テーブルのデザイン(主キーなど)とLEFT OUTER JOINクエリが最も効率的な方法でこの目標を達成しているかどうかです。

CREATE TABLE [PriceHistory] 
(
    [RowID] [int] IDENTITY(1,1) NOT NULL, 
    [ItemIdentifier] [varchar](10) NOT NULL, 
    [EffectiveDate] [date] NOT NULL, 
    [Price] [decimal](12, 2) NOT NULL, 

    CONSTRAINT [PK_PriceHistory] 
     PRIMARY KEY CLUSTERED ([ItemIdentifier] ASC, [RowID] DESC, [EffectiveDate] ASC) 
) 

INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-15',5.50) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-16',5.75) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-16',6.25) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-17',6.05) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-18',6.85) 
GO 

SELECT 
    L.EffectiveDate, L.Price 
FROM 
    [PriceHistory] L 
LEFT OUTER JOIN 
    [PriceHistory] R ON L.ItemIdentifier = R.ItemIdentifier 
        AND L.EffectiveDate = R.EffectiveDate 
        AND L.RowID < R.RowID 
WHERE 
    L.ItemIdentifier = 'ABC' and R.EffectiveDate is NULL 
ORDER BY 
    L.EffectiveDate 

フォローアップ:表は、価格データの価値dacadesでItemIdentifiers何千ものそれぞれを含むことができます。監査上の理由から、履歴バージョンのデータを保存する必要があります。テーブルにクエリを行い、レポート内のデータを使用するとします。レポートが生成された時点で私は@MRID = Max(RowID)を保存しています。今すぐ 'ABC'の価格が'2016-03-16 'に修正されたら、@MRIDを使用してクエリを変更し、以前実行したレポートを複製できます。

+0

は、なぜあなたは参加していますか?トップ1を選択するのに十分ですか? –

+1

ddlとサンプルデータを投稿していただき、ありがとうございます。それはとても簡単に役立ちます。私は皆がこのようにあなたの指導に従うことを望む! –

答えて

2

@ SeanLangeの答えを少し変更したバージョンではなく、製品ごとの、あなたの日付ごとの最後の行を与える:

with sortedResults as 
(
    select * 
     , ROW_NUMBER() over(PARTITION by ItemIdentifier, EffectiveDate 
          ORDER by ID desc) as RowNum 
    from PriceHistory 
) 

select ItemIdentifier, EffectiveDate, Price 
from sortedResults 
where RowNum = 1 
order by 2 
+1

これは明らかにOPが探していたものです。唯一の唯一の提案は、序数による注文ではないことです。列名で注文する方がより明確で安全です。 –

+0

@SeanLange私は同意します。順序位置でORDER BYを使用すると、コードが十分に読み込み不能になります。さらに、SELECTの列の順序を変更すると、クエリで間違った結果が返されます。 –

2

テーブルにItemIdentifierが2つ以上あるとします。データのバージョンをテーブルに保存するという点では、デザインには少し問題があります。しかし、このようなことを非常に簡単に実行して、各ItemIdentifierの最新のものを取得することができます。

with sortedResults as 
(
    select * 
     , ROW_NUMBER() over(PARTITION by ItemIdentifier order by EffectiveDate desc) as RowNum 
    from PriceHistory 
) 
select * 
from sortedResults 
where RowNum = 1 
+0

与えられたItemIdentifierの値だけが必要な場合は、 –

+0

このクエリは、私に 'ABC'の最新価格を与えるだけです。テーブルに存在する日付ごとに最新の価格が必要です。私が提供したクエリは正しい結果をもたらします:日付ごとに1つの価格しかない時系列。 – c31983

+0

これは、ABCのみを表示するように結果を制限するものではありません。テーブル全体の各ItemIdentifierの最新価格が表示されます。あなたが1つのアイテムだけを望むなら、あなたはTOP提案の1つを使うべきです。 –

1

短い回答、いいえ。

既存の索引に応じて、同じ表を2回押して、おそらくループ・テーブル・スキャンを作成しています。最善のケースでは、ループ・インデックス・シークを引き起こしてから、ほとんどの行をスローします。

これは、あなたが求めているものの中で最も効率的なクエリです。

SELECT 
    L.EffectiveDate, 
    L.Price 
FROM 
    (
     SELECT 
      L.EffectiveDate, 
      L.Price, 
      ROW_NUMBER() OVER (
       PARTITION BY 
        L.ItemIdentifier, 
        L.EffectiveDate 
       ORDER BY RowID DESC) RowNum 
     FROM [PriceHistory] L 
     WHERE L.ItemIdentifier = 'ABC' 
    ) L 
WHERE 
    L.RowNum = 1; 
+0

提案した最初のクエリは、単一のレコードのみを返します。私は各日付の最新の値を含む時系列が必要です。私の投稿に含まれているクエリは、正しい結果を生成します。私は、2番目の提案で参照されているCROSS APPLY演算子に精通しておらず、実行に問題があります。 [Item]はどこから来たのですか? – c31983

+0

これは基本的に、他の回答がどのように構成されているかに合わせて修正されました。この結果セットを別のテーブルに結合しようとしたり、大きなクエリで使用しようとすると、問題が発生します。 CROSS APPLYが出場するかもしれません。私はそれがあなたが探しているものの一部であるようには聞こえないので、その提案を削除しました。 –

関連する問題