2017-10-11 68 views
5

Zważywszy uporządkowanej tabeli z 11 kolumn:
timespan, val1, val2, val3, val4, val5, val6, val7, val8, val9 and val10Jak odzyskać "ostatnie" niezerowe wartości z wielu kolumn?

Załóżmy zbiór rekordów, takich jak:

timespan  val1 val2 val3 val4 val5 val6 val7 val8 val9 val10 
10/09/2011  0  0 60 80 40  0  0 40 80  0 
10/10/2011  0 10 90 30 70 50 50 70 30  90 
10/11/2011  10  0 20  0  0 60 60  0  0  20 

Potrzebuję zapytanie SQL (SQL Server 2012), która zwraca ostatni (w czasie) niezerowej wartości dla wszystkich kolumn, val1, wart2, ..., czyli

val1 val2 val3 val4 val5 val6 val7 val8 val9 val10 
    10 10 20 30 70 60 60 70 30  20 

podobne pytanie można znaleźć na Subquery: how to retrieve the last non-zero value from a column? ale to działa tylko dla jednej kolumny i uogólnienie, aby dodać więcej kolumn (jak w tym przypadku) wydaje się niepraktyczne.

Odpowiedz

1

Można użyć first_value():

select distinct first_value(val1) over (order by sign(val1) desc, timespan desc) as val1, 
     first_value(val2) over (order by sign(val2) desc, timespan desc) as val2, 
     . . . 
from t; 

Generalnie jestem przeciwny użyciu select distinct jako zamiennik dla zapytania agregacji. Niestety SQL Server obsługuje first_value() jako funkcję okna, ale nie zapewnia odpowiednika dla agregacji. Uwaga: funkcja służy do zerowania wartości zerowych. Jeśli możesz mieć wartości ujemne, użyj abs(sign()).

1

Inną opcją jest szybkie UNPIVOT następnie przegubu

przykładu

Select * 
From (
      Select top 1 with ties item,value 
      From YourTable 
      UnPivot (Value for Item in (val1,val2,val3,val4,val5,val6,val7,val8,val9,val10)) u 
      Where value<>0 
      Order by Row_Number() over (Partition By item Order by timespan desc) 
     ) src 
Pivot (max(value) For item in (val1,val2,val3,val4,val5,val6,val7,val8,val9,val10)) p 

Zwraca

val1 val2 val3 val4 val5 val6 val7 val8 val9 val10 
10  10  20  30  70  60  60  70  30  20 
+0

Dobre rozwiązanie, które działa pre-2012 – scsimon

+0

@scsimon Dzięki, czysty głupie szczęście, że obsługuje pre-2012. Szczerze mówiąc, nigdy o tym nie myślałem. :) –

0

można użyć coś jak poniżej. 1. Logika polega na pierwszym rozparciu wartości, a następnie usunięciu 0 pozycji, a następnie obliczeniu ostatniej niezerowej wartości jako wartość_wiersza = 1.

  1. Po ponownym obróceniu, aby uzyskać wynik.

Zapytanie jest poniżej

create table t 
(timespan date,val1 int,val2 int,val3 int,val4 int,val5 int,val6 int,val7 int,val8 int,val9 int,val10 int); 
insert into t values 
('10/09/2011', 0, 0,60,80,40, 0, 0,40,80, 0) 
,('10/10/2011', 0,10,90,30,70,50,50,70,30,90) 
,('10/11/2011',10, 0,20, 0, 0,60,60, 0, 0,20); 

select * 
    from 
(
    select 
     value, Columns 
    from 
    (
     select 
      timespan, 
      value, 
      Columns, 
      row_number() over(partition by Columns order by timespan desc) r 
     from 
     (select * from t)s 
     unpivot 
     ( 
      value for Columns in 
       ([val1],[val2],[val3],[val4],[val5],[val6],[val7],[val8],[val9],[val10]) 
     )up 
     where value<>0 
    ) t 
    where r=1 
)s 
pivot 
(
    max(value) for Columns in 
     ([val1],[val2],[val3],[val4],[val5],[val6],[val7],[val8],[val9],[val10]) 
)p 

See working demo