Sql server sql server中带有条件的Lag()

Sql server sql server中带有条件的Lag(),sql-server,lag,lead,Sql Server,Lag,Lead,我有一张这样的桌子: Number Price Type Date Time ------ ----- ---- ---------- --------- 23456 0,665 SV 2014/02/02 08:00:02 23457 1,3 EC 2014/02/02 07:50:45 23460 0,668 SV 2014/02/02 0

我有一张这样的桌子:

Number   Price    Type       Date         Time
------   -----    ----    ----------    ---------
23456    0,665     SV     2014/02/02     08:00:02
23457    1,3       EC     2014/02/02     07:50:45
23460    0,668     SV     2014/02/02     07:36:34
对于每个EC,我需要上一个/下一个SV价格。在这种情况下,查询很简单

Select Lag(price, 1, price) over (order by date desc, time desc),
Lead(price, 1, price) over (order by date desc, time desc)
from ITEMS
但是,在某些特殊情况下,两行或多行为EC类型:

Number   Price    Type       Date         Time
------   -----    ----    ----------    ---------
23456    0,665     SV     2014/02/02     08:00:02
23457    1,3       EC     2014/02/02     07:50:45
23658    2,4       EC     2014/02/02     07:50:45
23660    2,4       EC     2014/02/02     07:50:48
23465    0,668     SV     2014/02/02     07:36:34 

在这种情况下,我可以使用超前/滞后吗?如果不是,我是否必须使用子查询

是的,您可以使用超前/滞后。你只需要用一个小小的行号魔术预先计算一下要跳多远

DECLARE @a TABLE ( number int, price money, type varchar(2),
                   date date, time time)
INSERT @a VALUES
(23456,0.665,'SV','2014/02/02','08:00:02'),
(23457,1.3  ,'EC','2014/02/02','07:50:45'),
(23658,2.4  ,'EC','2014/02/02','07:50:45'),
(23660,2.4  ,'EC','2014/02/02','07:50:48'),
(23465,0.668,'SV','2014/02/02','07:36:34');

; WITH a AS (
     SELECT *,
            ROW_NUMBER() OVER(ORDER BY [date] DESC, [time] DESC) x, 
            ROW_NUMBER() OVER(PARTITION BY 
               CASE [type] WHEN 'SV' THEN 1 ELSE 0 END 
               ORDER BY [date] DESC, [time] DESC) y 
     FROM @a)
 , b AS (
     SELECT *,
            ROW_NUMBER() OVER(PARTITION BY x-y ORDER BY x ASC) z1,
            ROW_NUMBER() OVER(PARTITION BY x-y ORDER BY x DESC) z2 
     FROM a)
SELECT *,
       CASE [type] WHEN 'SV' THEN 
           LAG(price,z1,price) OVER(PARTITION BY [type] ORDER BY x) 
           ELSE LAG(price,z1,price) OVER(ORDER BY x) 
           END,
       CASE [type] WHEN 'SV' THEN 
           LEAD(price,z2,price) OVER(PARTITION BY [type] ORDER BY x) 
           ELSE LEAD(price,z2,price) OVER(ORDER BY x) 
           END
FROM b
ORDER BY x

你的问题和Anon出色的回答是其中的一部分。在这个答案中,我将尝试详细研究行数魔法

我根据球赛中的事件做了一个简单的例子。对于每个活动,我们都要打印上一季度和下一季度的相关信息:

create table TestTable (id int identity, event varchar(64));
insert TestTable values
    ('Start of Q1'),
    ('Free kick'),
    ('Goal'),
    ('End of Q1'),
    ('Start of Q2'),
    ('Penalty'),
    ('Miss'),
    ('Yellow card'),
    ('End of Q2');
下面是一个显示行数魔术方法的查询:

; with  grouped as
        (
        select  *
        ,       row_number() over (order by id) as rn1
        ,       row_number() over (
                    partition by case when event like '%of Q[1-4]' then 1 end 
                    order by id) as rn2
        from    TestTable
        )
,       order_in_group as
        (
        select  *
        ,       rn1-rn2 as group_nr
        ,       row_number() over (partition by rn1-rn2 order by id) as rank_asc
        ,       row_number() over (partition by rn1-rn2 order by id desc)
                    as rank_desc
        from    grouped
        )
select  *
,       lag(event, rank_asc) over (order by id) as last_event_of_prev_group
,       lead(event, rank_desc) over (order by id) as first_event_of_next_group
from    order_in_group
order by
        id
第一个名为“分组”的CTE计算两个行号。第一个是表中每行1 2 3。第二行将暂停公告放在一个列表中,其他事件放在第二个列表中。两者之间的区别,rn1-rn2,对于游戏的每个部分都是独一无二的。检查示例输出中的差异很有帮助:它位于group_nr列中。您将看到每个值对应于游戏的一个部分。 _组中称为order_的第二个CTE确定当前行在其孤岛或间隙中的位置。对于具有3行的孤岛,升序为1 2 3,降序为3 2 1。 最后,我们有足够的知识来判断滞后和领先的跳跃距离。我们必须延迟rank_asc行,才能找到上一节的最后一行。要找到下一节的第一行,我们必须在rank_desc行前面。
希望这有助于澄清缺口和岛屿的魔力

这里是实现相同结果的另一种方法,但使用顺序窗口上的条件最大/最小函数。序号可以根据符合目的的任何列设置,但在这种情况下,我相信OP希望它们是日期和时间

请注意,OP给出的示例已扩展,以显示EC的散布序列符合预期结果。在这种情况下,由SV类型的最早两个连续行引入的歧义将导致选取最大值。将序号设置为包含价格是改变这种行为的一种可能方式


在这里可以找到SQLFIDLE:

很好的解决方案,但是我的RDBMS Netezza抱怨lead函数的偏移量orgument必须是一个正整数常量。
DROP TABLE IF EXISTS #t;
CREATE TABLE #t (
    Number INT, 
    Price MONEY, 
    Type CHAR(2),
    Date DATE,
    Time TIME(0)
);
INSERT INTO #t VALUES 
(23456, 0.666, 'SV', '2014/02/02', '10:00:02'),
(23457, 1.4  , 'EC', '2014/02/02', '09:50:45'),
(23658, 2.5  , 'EC', '2014/02/02', '09:50:45'),
(23660, 2.5  , 'EC', '2014/02/02', '09:50:48'),
(23465, 0.669, 'SV', '2014/02/02', '09:36:34'),
(23456, 0.665, 'SV', '2014/02/02', '08:00:02'),
(23457, 1.3  , 'EC', '2014/02/02', '07:50:45'),
(23658, 2.4  , 'EC', '2014/02/02', '07:50:45'),
(23660, 2.4  , 'EC', '2014/02/02', '07:50:48'),
(23465, 0.668, 'SV', '2014/02/02', '07:36:34'), -- which one of these?
(23465, 0.670, 'SV', '2014/02/02', '07:36:34'); -- 

WITH time_ordered AS (
    SELECT *, DENSE_RANK() OVER (ORDER BY Date, Time) AS ordinal FROM #t
)
SELECT
    *,
    CASE WHEN Type = 'EC' 
    THEN MAX(CASE WHEN ordinal = preceding_non_EC_ordinal THEN Price END) 
    OVER (PARTITION BY preceding_non_EC_ordinal ORDER BY ordinal ASC) END AS preceding_price,
    CASE WHEN Type = 'EC'
    THEN MIN(CASE WHEN ordinal = following_non_EC_ordinal THEN Price END) 
    OVER (PARTITION BY following_non_EC_ordinal ORDER BY ordinal DESC) END AS following_price
FROM (
    SELECT
        *,
        MAX(CASE WHEN Type <> 'EC' THEN ordinal END) 
        OVER (ORDER BY ordinal ASC) AS preceding_non_EC_ordinal,
        MIN(CASE WHEN Type <> 'EC' THEN ordinal END) 
        OVER (ORDER BY ordinal DESC) AS following_non_EC_ordinal
    FROM time_ordered
) t
ORDER BY Date, Time