2015-06-11 12 views
5

Jestem zobowiązany do używania PostgresSql. Mam podane w poniższej tabeli nazywa pieniądze jako wejściePokaż wszystkie kolumny zmieniające się między dwoma wierszami

Cash | Adhoc | Collateral | Total 
--------------------------------- 
20 | 30 | 40  | 90 
32 | 12 | 40  | 84 
10 | 12 | 40  | 62 
13 | 20 | 50  | 83 

Jak sama nazwa wskazuje, jest całkowita suma gotówki, adhoc i zabezpieczeń wartości tego wiersza.

I wymagają follwing tabeli wyjściowej

ChangeType | ChangeAmount 
--------------------------- 
    Cash |  12 
    Adhoc |  -18 
    Cash |  -22 
    Cash |  3 
    Adhoc |  8 
Collateral |  10 

Jest to etap 1. W następnym etapie, nowa kolumna o nazwie ClientID ma zostać dodany, a zmiany te mają być pokazane dla każdego konkretnego klienta. Powiedz, że klient1 jest wierszem 1, klient 2 to wiersz 2 i 3, a następnie klient1 znów jest wierszem 4. Następnie tabela porównawcza dla klienta1 zostanie utworzona przy użyciu wiersza1 i wiersza4.

tabela wyjściowa będzie

ChangeType | ChangeAmount | ClientId 
------------------------------------------ 
    Cash  |  7  | client1 
    Adhoc  |  -10  | client1 
Collateral |  10  | client1 
    Cash  |  -22  | client2 

udało mi się osiągnąć to, co jest chaging gdy „tylko jedna kolumna” zmienia się między rzędami za pomocą

SELECT 
CASE WHEN ((Cash - lag(Cash, 1)    
      OVER (PARTITION BY clientId 
      )) != 0) 
    THEN CAST('Cash' AS Text)  
    WHEN ((Adhoc - lag(Adhoc, 1)    
      OVER (PARTITION BY clientId 
      )) != 0) 
    THEN CAST('Adhoc' AS Text) 
    WHEN ((Collateral - lag(Collateral, 1)    
      OVER (PARTITION BY clientId 
      )) != 0) 
    THEN CAST('Collateral' AS Text) 
    END, 
    Total - lag(Total,1) 
    OVER (PARTITION BY clientId) 
    FROM money             

Ja jednak stracił jak aby pokazać zmianę dla wielu kolumn zmieniających się w jednym rzędzie.

+1

Jest to szczególny przypadek unpivoting. Najprawdopodobniej musisz wygenerować rekordy zmian dla każdej kolumny każdego wiersza, a następnie odfiltrować te, w których zmiana wynosi zero. –

+0

Dzięki. Czy niepodzielenie jest wykonywane na poziomie zapytania, czy też muszę napisać kod soci C++? –

+0

Byłbym bardzo zaskoczony, gdyby nie było to praktyczne na poziomie SQL. Podaj użyteczne, kompletne dane przykładowe jako instrukcje "CREATE TABLE" i "INSERT" (najlepiej jako http://sqlfiddle.com) i przyjrzę się, ale nie mam ochoty wymyślać osobliwych danych. –

Odpowiedz

2

W niektórych przypadkach metody proceduralne są znacznie łatwiejsze niż czysty SQL. Myślę, że tak jest. Wypróbuj funkcję poniżej:

create or replace function show_money_changes() 
returns table (change_type text, change_amount integer) 
language plpgsql 
as $$ 
declare 
    prev record; 
    curr record; 
    frst boolean = true; 
begin 
    for curr in select * from money --order by id 
    loop 
     if not frst then 
      if curr.cash <> prev.cash then 
       return query select 'cash'::text, curr.cash - prev.cash; 
      end if; 
      if curr.adhoc <> prev.adhoc then 
       return query select 'adhoc'::text, curr.adhoc - prev.adhoc; 
      end if; 
      if curr.collateral <> prev.collateral then 
       return query select 'collateral'::text, curr.collateral - prev.collateral; 
      end if; 
     end if; 
     prev = curr; 
     frst = false; 
    end loop; 
end $$; 

select * from show_money_changes() 

Uwaga: należy mieć kolumnę (słownie id) w money tabeli jednoznacznie zamówić wiersze.


Czysta rozwiązanie SQL (przy założeniu, że tabela ma kolumnę id z kolejnymi numerami):

select * from (
    select 
     unnest(array['cash', 'adhoc', 'collateral']) change_type, 
     unnest(array[m2.cash- m1.cash, m2.adhoc- m1.adhoc, m2.collateral- m1.collateral]) change_value 
    from money m1 
    join money m2 on m1.id+ 1 = m2.id 
    ) alias 
where change_value <> 0 

musisz zmienić warunek

on m1.id+ 1 = m2.id 

(łączenia aktualny rząd z rzędem następnie) zgodnie z rzeczywistą definicją tabeli.

Do tego celu można użyć row_number(). Powiedzmy event_time jest kolumna na zamówienie przez, a następnie:

with money_with_row_numbers as (
    select *, row_number() over (order by event_time) rn 
    from money) 
select * from (
    select 
     unnest(array['cash', 'adhoc', 'collateral']) change_type, 
     unnest(array[m2.cash- m1.cash, m2.adhoc- m1.adhoc, m2.collateral- m1.collateral]) change_value 
    from money_with_row_numbers m1 
    join money_with_row_numbers m2 on m1.rn+ 1 = m2.rn 
    ) alias 
where change_value <> 0 
+0

Mam kolumnę znacznika czasu, aby zamówić wiersze. Nie pokazałem tego tutaj, ponieważ czułem (co dyskusyjne), że nie dodało to wiele do problemu –

+0

@PranavKapoor - więc dodaj właściwe 'order by' w select. – klin

Powiązane problemy