使用SQL有效地查询最新版本的记录

时间:2016-03-21 21:13:06

标签: sql-server outer-join

我需要在表中查询所有可用日期(日终时间序列)的最新记录版本。下面的例子说明了我想要实现的目标。

我的问题是表格的设计(主键等)和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

跟进:表可以包含数千个ItemIdentifier,每个ItemIdentifier都有价值数据的dacade。出于审计原因,需要保留历史版本的数据。假设我查询表并使用报告中的数据。我在生成报告时存储@MRID = Max(RowID)。现在,如果'2016-03-16'上'ABC'的价格在以后更正,我可以使用@MRID修改查询并复制我之前运行的报告。

3 个答案:

答案 0 :(得分:2)

我假设你的表中有超过1个ItemIdentifier。您的设计有点问题,因为您在表中保留了数据的版本。但是,您可以非常轻松地执行此类操作,以获取每个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

答案 1 :(得分: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

答案 2 :(得分:1)

简短回答,不。

您正在两次访问同一个表,并且可能会创建一个循环表扫描,具体取决于您现有的索引。在最好的情况下,您将导致循环索引搜索,然后丢弃大部分行。

这将是您提出的最有效的查询。

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;