TSQL从物料价格变化获取物料价格历史记录

时间:2018-06-27 15:37:58

标签: tsql sql-server-2016

我有一个商品价格变化表,我想用它来创建每个日期(商品的开始日期和结束日期之间)的商品价格表。

以下是创建日期的代码:-

declare @Item table (item_id int, item_launch_date date, item_end_date date); 

insert into @Item Values (1,'2001-01-01','2016-01-01'), (2,'2001-01-01','2016-01-01')

declare @ItemPriceChanges table (item_id int, item_price money, my_date date); 

INSERT INTO @ItemPriceChanges VALUES (1, 123.45, '2001-01-01'), (1, 345.34, '2001-01-03'), (2, 34.34, '2001-01-01'), (2,23.56 , '2005-01-01'), (2, 56.45, '2016-05-01'), (2, 45.45, '2017-05-01'); ;

我想看到的是这样的:-

item_id  date       price
-------  ----       -----
1        2001-01-01 123.45
1        2001-01-02 123.45
1        2001-01-03 345.34
1        2001-01-04 345.34
etc.
2        2001-01-01  34.34
2        2001-01-02  34.34
etc.

关于如何编写查询的任何建议? 我正在使用SQL Server 2016。

已添加: 我还有一个名为“ dim_calendar”的日历表,每天有一行。我本来希望使用开窗函数,但我能找到的最接近的是lead(),它并没有实现我想的那样:-

select 
    i.item_id,
    c.day_date,
    ipc.item_price as item_price_change,
    lead(item_price,1,NULL) over (partition by i.item_id ORDER BY c.day_date) as item_price
from dim_calendar c
inner join @Item i
on c.day_date between i.item_launch_date and i.item_end_date
left join @ItemPriceChanges ipc
on i.item_id=ipc.item_id
and ipc.my_date=c.day_date
order by 
    i.item_id,
    c.day_date;

谢谢

2 个答案:

答案 0 :(得分:2)

在您进行编辑之前,我曾写过这篇文章。请注意,您的样本输出表明,一个商品在价格变动的当天可以有两个价格。以下假设一个商品在价格变动日只能有一个价格,那就是新价格。

declare @Item table (item_id int, item_launch_date date, item_end_date date); 

insert into @Item Values (1,'2001-01-01','2016-01-01'), (2,'2001-01-01','2016-01-01')

declare @ItemPriceChange table (item_id int, item_price money, my_date date); 

INSERT INTO @ItemPriceChange VALUES (1, 123.45, '2001-01-01'), (1, 345.34, '2001-01-03'), (2, 34.34, '2001-01-01'), (2,23.56 , '2005-01-01'), (2, 56.45, '2016-05-01'), (2, 45.45, '2017-05-01'); 

SELECT * FROM @ItemPriceChange

-- We need a table variable holding all possible date points for the output

DECLARE @DatePointList table (DatePoint date);

DECLARE @StartDatePoint date = '01-Jan-2001';

DECLARE @MaxDatePoint date = GETDATE();

DECLARE @DatePoint date = @StartDatePoint;

WHILE @DatePoint <= @MaxDatePoint BEGIN

    INSERT INTO @DatePointList (DatePoint)
        SELECT @DatePoint;

    SET @DatePoint = DATEADD(DAY,1,@DatePoint);

END;

-- We can use a CTE to sequence the price changes

WITH ItemPriceChange AS (
    SELECT item_id, item_price, my_date, ROW_NUMBER () OVER (PARTITION BY Item_id ORDER BY my_date ASC) AS SeqNo
    FROM @ItemPriceChange
)

-- With the price changes sequenced, we can derive from and to dates for each price and use a join to the table of date points to produce the output. Also, use an inner join back to @item to only return rows for dates that are within the start/end date of the item

SELECT ItemPriceDate.item_id, DatePointList.DatePoint, ItemPriceDate.item_price
FROM @DatePointList AS DatePointList
INNER JOIN (
    SELECT ItemPriceChange.item_id, ItemPriceChange.item_price, ItemPriceChange.my_date AS from_date, ISNULL(ItemPriceChange_Next.my_date,@MaxDatePoint) AS to_date
    FROM ItemPriceChange
    LEFT OUTER JOIN ItemPriceChange AS ItemPriceChange_Next ON ItemPriceChange_Next.item_id = ItemPriceChange.item_id AND ItemPriceChange.SeqNo = ItemPriceChange_Next.SeqNo - 1
) AS ItemPriceDate ON DatePointList.DatePoint >= ItemPriceDate.from_date AND  DatePointList.DatePoint < ItemPriceDate.to_date
INNER JOIN @item AS item ON item.item_id = ItemPriceDate.item_id AND DatePointList.DatePoint BETWEEN item.item_launch_date AND item.item_end_date
ORDER BY ItemPriceDate.item_id, DatePointList.DatePoint;

答案 1 :(得分:0)

@AlphaStarOne完美!我已经对其进行了修改,以使用Windowing函数而不是自连接,但是您建议的方法是可行的。如果其他人需要它,这是我的实现:

SELECT 
    ipd.item_id, 
    dc.day_date, 
    ipd.item_price
FROM dim_calendar dc
INNER JOIN (
    SELECT 
        item_id, 
        item_price, 
        my_date AS from_date,
        isnull(lead(my_date,1,NULL) over (partition by item_id ORDER BY my_date),getdate()) as to_date
    FROM @ItemPriceChange ipc1
) AS ipd 
ON dc.day_date >= ipd.from_date 
AND dc.day_date < ipd.to_date
INNER JOIN @item AS i 
ON i.item_id = ipd.item_id 
AND dc.day_date BETWEEN i.item_launch_date AND i.item_end_date
ORDER BY 
    ipd.item_id, 
    dc.day_date;