我有一个商品价格变化表,我想用它来创建每个日期(商品的开始日期和结束日期之间)的商品价格表。
以下是创建日期的代码:-
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;
谢谢
答案 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;