如何根据特定条件选择行和附近的行

时间:2019-03-22 14:05:07

标签: sql

我有一个值表(Trans)

OrderID (unique) | CustID  | OrderDate| TimeSinceLast|                  
------------------------------------------------------
    123a         | A01     | 20.06.18 |    20        |
    123y         | B05     | 20.06.18 |    31        |
    113k         | A01     | 18.05.18 |    NULL      | <------- need this
    168x         | C01     | 17.04.18 |    8         |
    999y         | B05     | 15.04.18 |    NULL      | <------- need this
    188k         | A01     | 15.04.18 |   123        |
    678a         | B05     | 16.03.18 |    45        |

我需要选择TimeSinceLast为null的行以及TimeSinceLast不为null的前后行,并按custID分组

我需要我的决赛桌看起来像:

    OrderID (unique) | CustID  | OrderDate| TimeSinceLast|                  
    ------------------------------------------------------
        123a         | A01     | 20.06.18 |    20        | 
        113k         | A01     | 18.05.18 |    NULL      | 
        188k         | A01     | 15.04.18 |   123        | 
        123y         | B05     | 20.06.18 |    31        |
        999y         | B05     | 15.04.18 |    NULL      |
        678a         | B05     | 16.03.18 |    45        |

主要问题是TimeSinceLast不可靠,并且由于任何原因,自上次订购以来的日子都不能很好地计算出来,因此我无法在查询中将其用于上一行或下一行。 我试图寻找代码,并在此论坛上找到了类似的内容

 with dt as
       (select distinct custID, OrderID,
        max (case when timeSinceLast is null then OrderID end)
        over(partition by custID order by OrderDate
             rows between 1 preceding and 1 following) as NullID
        from Trans)
    select *
    from dt
    where request_id between NullID -1 and NullID+1

但是对于我的目的而言效果不佳。而且看起来max函数无法使用缺少的值。

非常感谢

2 个答案:

答案 0 :(得分:0)

使用lead()lag()

  

我需要选择TimeSinceLast为null的行,以及TimeSinceLast不为null的前后的行。

首先,排序还不清楚。您的示例数据和代码不匹配。以下内容假定日期和订单编号为某种组合,但是可能还有其他列可以更好地捕获“在前”和“在后”的含义。

这有点棘手,因为除非总要,否则您不希望总是包含第一行和最后一行。因此,请看两列:

select t.*
from (select t.*,
             lead(TimeSinceLast) over (partition by custid order by orderdate, orderid) as next_tsl,
             lag(TimeSinceLast) over (partition by custid order by orderdate, orderid) as prev_tsl,
             lead(orderid) over (partition by custid order by orderdate, orderid) as next_orderid,
             lag(orderid) over (partition by custid order by orderdate, orderid) as prev_orderid
      from t
     ) t
where TimeSinceLast is not null or
      (next_tsl is null and next_orderid is not null) or
      (prev_tsl is null and prev_orderid is not null);

答案 1 :(得分:0)

使用申请

DECLARE @TransTable TABLE (OrderID char(4), CustID char(3), OrderDate date, TimeSinceLast int)

INSERT @TransTable VALUES
('123a', 'A01', '06.20.2018', 20),
('123y', 'B05', '06.20.2018' ,31),
('113k', 'A01', '05.18.2018' ,NULL), ------- need this
('168x', 'C01', '04.17.2018' ,8),
('999y', 'B05', '04.15.2018' ,NULL), ------- need this
('188k', 'A01', '04.15.2018' ,123),
('678a', 'B05', '03.16.2018' ,45)

SELECT B.OrderID, B.CustID, B.OrderDate, B.TimeSinceLast 
FROM @TransTable A
CROSS APPLY (
    SELECT 0 AS rn, A.OrderID, A.CustID, A.OrderDate, A.TimeSinceLast
    UNION ALL
    SELECT TOP 2 ROW_NUMBER() OVER (PARTITION BY CASE WHEN T.OrderDate > A.OrderDate THEN 1 ELSE 0 END ORDER BY ABS(DATEDIFF(day, T.OrderDate, A.OrderDate))) rn, 
    T.OrderID, T.CustID, T.OrderDate, T.TimeSinceLast
    FROM @TransTable T 
    WHERE T.CustID = A.CustID AND T.OrderID <> A.OrderID
    ORDER BY rn
) B
WHERE A.TimeSinceLast IS NULL
ORDER BY B.CustID, B.OrderDate DESC