在t-sql中流畅地添加值

时间:2017-01-06 20:20:05

标签: sql sql-server tsql

我有一张这样的表:

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

不应该用游标制作。帮助表可以。

4 个答案:

答案 0 :(得分:3)

这就是您希望ignore nullslag()上的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 ;

复杂算术只是计算差异,除以天数,然后将天数分配到当天。

答案 1 :(得分: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和price捆绑在一个列中(RnPr上面)。 RnPr的顺序与row_number的顺序相同。 MINMAX都忽略了NULLS。因此,如果当前行中的价格为空,则在MAX(RnPr)之间找到UNBOUNDED PRECEDING AND CURRENT ROW将包含先前NOT NULL价格的值。同样MIN(RnPr)会在CURRENT ROW AND UNBOUNDED FOLLOWING之间找到下一个框架。

然后可以将其分解以获得如上所述的价格和行号。

如果对结果感到满意,则可以移除最终SELECT,并在this demo中取消注释UPDATE

答案 2 :(得分:0)

只需更换&#34; YourTable&#34; (其中4个)带有您的实际表名。

如果您对结果感到满意,请注释掉选择并取消评论UPDATEWHERE

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

返回

现在,您将注意到项目1在01/06和01/08之间的空值。这是因为没有用于插值的上限。

enter image description here

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