2014-02-14 39 views
8

我有這樣的一個表:滯後()與conditon在SQL Server

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 

我可以用超前/滯後在此情況下?如果不是,我是否必須使用子查詢?

感謝

回答

4

是的,你可以使用LEAD/LAG。你只需要預先計算用多少ROW_NUMBER()魔法跳得有多遠。

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 
+0

偉大的解決方案,但我的RDBMS(Netezza)抱怨,領導函數的偏移量必須是「正整數常量」。 – stevepastelan

7

您的問題(和Anon的優秀答案)是the SQL of islands and gaps的一部分。在這個答案中,我將嘗試詳細檢查「row_number()magic」。

我已經根據球賽中的事件做了一個簡單的例子。對於每個事件,我們想打印一個和下一個季度的相關消息:

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'); 

這裏有一個查詢顯示「ROW_NUMBER()魔術」的方式關閉:

; 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計算兩個row_number() s。首先是1 2 3表中的每一行。第二個row_number()將暫停通知列入一個列表,其他事件列入第二個列表。兩者之間的差異,rn1 - rn2,對於遊戲的每個部分都是獨一無二的。檢查示例輸出中的差異會很有幫助:它位於group_nr列中。你會看到每個值對應於遊戲的一部分。
  • 稱爲「order_in_group」的第二個CTE確定當前行在其島或間隙內的位置。對於3行的島嶼,升序爲1 2 3,降序爲3 2 1
  • 最後,我們知道足以告訴lag()lead()要跳多遠。我們必須落後rank_asc行才能找到上一節的最後一行。要找到下一部分的第一行,我們必須領導rank_desc行。

希望這有助於澄清差距和島嶼的「魔力」。