2017-01-06 2 views
4

У меня есть таблица вроде этого:Fluently добавляющие значения в T-SQL

Items Date  Price 
1  2016-01-01 10 
1  2016-01-02 15 
1  2016-01-03 null 
1  2016-01-04 null 
1  2016-01-05 8 
1  2016-01-06 null 
1  2016-01-07 null 
1  2016-01-08 null 
2  2016-01-01 14 
2  2016-01-02 7 
2  2016-01-03 null 
2  2016-01-04 null 
2  2016-01-05 16 
2  2016-01-06 null 
2  2016-01-07 null 
2  2016-01-08 5 

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

Пример:

1  2016-01-02 15 to 
1  2016-01-05 8 

15 до 8 = -7

-7/3 = -2,333333

1  2016-01-02 15 
1  2016-01-03 12,6666 
1  2016-01-04 10,3333 
1  2016-01-05 8 

не должны быть сделаны с курсорами. Рецепты были бы в порядке.

ответ

3

Это действительно то, где вы хотите вариант ignore nulls на lag() и lead(). Увы.

В качестве альтернативы можно использовать outer apply:

select t.*, 
     coalesce(t.price, 
       tprev.price + 
       datediff(day, tprev.date, t.date) * (tnext.price - tprev.price)/datediff(day, tprev.date, tnext.date) 
       ) as est_price 
from t outer apply 
    (select top 1 t2.* 
     from t t2 
     where t2.item = t.item and 
      t2.date <= t.date and 
      t2.price is not null 
     order by t2.date desc 
    ) tprev outer apply 
    (select top 1 t2.* 
     from t t2 
     where t2.item = t.item and 
      t2.date >= t.date and 
      t2.price is not null 
     order by t2.date asc 
    ) tnext ; 

Комплекс арифметика просто вычисления разности, деления на количество дней, а затем выделить дни до текущего дня.

+0

Im kinda новичок в t-sql. никогда не видел такой код. спасибо – sasch0112

+0

Просто используйте 't.price' вместо' price' после coalesce;). –

1
WITH T1 AS 
(
SELECT *, 
     ROW_NUMBER() OVER (PARTITION BY Items ORDER BY Date) AS RN, 
     FORMAT(ROW_NUMBER() OVER (PARTITION BY Items ORDER BY Date),'D10') + FORMAT(Price,'0000000000.000000') AS RnPr 
FROM YourTable 
), T2 AS 
(
SELECT *, 
     MAX(RnPr) OVER (PARTITION BY Items ORDER BY Date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS prev, 
     MIN(RnPr) OVER (PARTITION BY Items ORDER BY Date ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS next 
FROM T1 
), T3 AS 
(
SELECT Items, 
     Date, 
     Price, 
     RnPr, 
     InterpolatedPrice = IIF(Price IS NOT NULL,prevPrice,prevPrice + (RN - prevRN) * (nextPrice - prevPrice)/NULLIF(nextRN - prevRN,0)) 
FROM T2 
CROSS APPLY (VALUES(CAST(SUBSTRING(prev,11,17) AS decimal(16,6)), 
        CAST(LEFT(prev, 10) AS INT), 
        CAST(SUBSTRING(next,11,17) AS decimal(16,6)), 
        CAST(LEFT(next, 10) AS INT) 
        )) V(prevPrice,prevRN,nextPrice,nextRN) 
) 
--UPDATE T3 SET Price = InterpolatedPrice 
SELECT * 
FROM T3 
ORDER BY Items, 
      Date 

Который возвращает

enter image description here

в row_number и цену связаны вместе в одной колонке (RnPr выше). Порядок RnPr совпадает с порядковым номером row_number. MIN и MAX обе игнорируют NULLS. Таким образом, поиск MAX(RnPr) между UNBOUNDED PRECEDING AND CURRENT ROW будет включать в себя значение предыдущей цены NOT NULL, если цена в текущей строке равна нулю. И аналогично MIN(RnPr) найдет следующий с рамкой между CURRENT ROW AND UNBOUNDED FOLLOWING.

Это может быть расколото, чтобы получить цену и номер строки, как указано выше.

Если вы довольны результатами, то окончательный SELECT может быть удален, а UPDATE ракомментирован как в this demo.

0

Просто замените «YourTable» (4 из них) на свое фактическое имя таблицы.

Если вы счастливы с результатами, то закомментируйте выбор и не комментируйте UPDATE и WHERE.

Select A.Items,A.Date, 
--Update YourTable Set 
     Price = IsNull(A.Price,((DateDiff(DD,B.Date,A.Date)/(DateDiff(DD,B.Date,C.Date)+0.0))*(C.Price - B.Price)) + B.Price) 
From YourTable A 
Outer Apply (Select Top 1 Date,Price from YourTable Where Items=A.Items and Date<A.Date and Price is not Null and A.Price is null Order by Price Desc) B 
Outer Apply (Select Top 1 Date,Price from YourTable Where Items=A.Items and Date>A.Date and Price is not Null and A.Price is null Order by Price) C 
--Where Price is NULL 

Возвращает

Теперь вы заметите аннулирует между 01/06 и 01/08 для элементов 1. Это потому, что нет колпачка интерполировать с.

enter image description here

0

Вы можете сделать это, используя ряд функций окна в общих табличных выражений.

  • T1 присваивает номер строки к каждой строке (rn)
  • T2 находит первый не нулевой Price номер строки до и после текущей строки (rnb/rna)
  • T3 вычисляет скорректированное Price, глядя до и после

declare @T table (Items int, Date date, Price float) 
insert into @T (Items, Date, Price) values 
(1, '2016-01-01', 10), (1, '2016-01-02', 15), (1, '2016-01-03', null), (1, '2016-01-04', null), (1, '2016-01-05', 8), (1, '2016-01-06', null), (1, '2016-01-07', null), (1, '2016-01-08', null), (2, '2016-01-01', 14), (2, '2016-01-02', 7), (2, '2016-01-03', null), (2, '2016-01-04', null), (2, '2016-01-05', 16), (2, '2016-01-06', null), (2, '2016-01-07', null), (2, '2016-01-08', 5) 

;with T1 as 
(
    select *, 
     row_number() over (order by Items, Date) as rn 
    from @T  
), 
T2 as 
(
    select *, 
     max(case when price is null then null else rn end) over (partition by Items order by Date) as rnb, 
     min(case when price is null then null else rn end) over (partition by Items order by Date desc) as rna 
    from T1 
) 
select Items, Date, 

    isnull(price, 
     lag(Price, rn-rnb, Price) over (order by rn) - 
     (
      lag(Price, rn-rnb, Price) over (order by rn) - 
      lead(Price, rna-rn, Price) over (order by rn) 
     )/(rna-rnb) * (rn-rnb)   
    ) as Price 

from T2 
order by Items, Date 
Смежные вопросы