2014-02-14 10 views
8

Ich habe eine Tabelle wie folgt aus:Lag() mit conditon in 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 

Für jede EC I vorherigen/nächsten SV Preis benötigen. In diesem Fall ist die Abfrage einfach.

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 

Aber gibt es einige spezielle Fälle, in denen zwei oder mehr Reihen EG sind:

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 

kann ich in diesen Fällen Blei/Lag verwenden? Wenn nicht, musste ich eine Unterabfrage verwenden?

Danke

Antwort

4

Ja, Sie können LEAD/LAG verwenden. Sie müssen nur vorberechnen, wie weit Sie mit einer kleinen ROW_NUMBER() -Magie springen sollen.

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

Großartige Lösung, aber mein RDBMS (Netezza) beschwert sich, dass das Offset-Orgument zur Lead-Funktion eine "positive Integer-Konstante" sein muss. – stevepastelan

7

Ihre Frage (und Anon ausgezeichnete Antwort) ist Teil von the SQL of islands and gaps. In dieser Antwort werde ich versuchen, die "row_number() Magie" im Detail zu untersuchen.

Ich habe ein einfaches Beispiel basierend auf Ereignissen in einem Ballspiel gemacht. Für jedes Ereignis möchten wir die vorherige und nächste Quartal bezogene Nachricht drucken:

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

Hier ist eine Abfrage vorführt die „row_number() magischen“ -Ansatz:

; 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 
  • die erste CTE genannt "gruppiert" berechnet zwei row_number() s. Die erste ist 1 2 3 für jede Zeile in der Tabelle. Die zweite row_number() platziert Pauseansagen in einer Liste und andere Ereignisse in einer zweiten Liste. Der Unterschied zwischen den beiden, rn1 - rn2, ist einzigartig für jeden Abschnitt des Spiels. Es ist hilfreich, den Unterschied in der Beispielausgabe zu überprüfen: Er befindet sich in der Spalte group_nr. Sie werden sehen, dass jeder Wert einem Abschnitt des Spiels entspricht.
  • Der zweite CTE namens "order_in_group" bestimmt die Position der aktuellen Zeile innerhalb ihrer Insel oder Lücke. Bei einer Insel mit 3 Zeilen sind die Positionen 1 2 3 für die aufsteigende Reihenfolge und 3 2 1 für die absteigende Reihenfolge.
  • Schließlich wissen wir genug zu sagen lag() und lead() wie weit zu springen. Wir müssen rank_asc Zeilen nacheilen, um die letzte Zeile des vorherigen Abschnitts zu finden. Um die erste Zeile des nächsten Abschnitts zu finden, müssen wir rank_desc Zeilen führen.

Hoffe dies hilft, die "Magie" von Gaps und Inseln zu klären. Here is a working example at SQL Fiddle.