TSQL Получить историю цен товара из изменений цены товара - PullRequest
0 голосов
/ 27 июня 2018

У меня есть таблица изменений цен на товары, и я хочу использовать ее для создания таблицы цен на товары для каждой даты (между датами начала и окончания товара).

Вот код для создания даты: -

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 голосов
/ 27 июня 2018

@ 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;
0 голосов
/ 27 июня 2018

Я написал это до вашего редактирования. Обратите внимание, что ваш пример вывода показывает, что у элемента могут быть две цены в день изменения цены. Ниже предполагается, что у предмета может быть только одна цена в день изменения цены, и это новая цена.

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;
...