Use of LAG - SQL Server 2014 - sql

I need to calculate a column (Transparencia) as the sum of the previous row's Transparencia and Dif values. Initially, only the first row has a value in the Transparencia column:
Account ------ Year_ ---- Month_ ---- Transparencia ---- Dif
--------------------------------------------------------------
'4030003'------ 2018 ---- 5 ---- 100 ---- -2
'4040001'------ 2018 ---- 5 ---- null ---- -4
'4040002'------ 2018 ---- 5 ---- null ---- 3
...
Account(N-1)------ 2018 ---- 5 ---- x ---- 8
Account(N)------ 2018 ---- 5 ---- x + 8 ---- 11
The aim is to get the following:
Account ------ Year_ ---- Month_ ---- Transparencia ---- Dif
--------------------------------------------------------------
'4030003'------ 2018 ---- 5 ---- 100 ---- -2
'4040001'------ 2018 ---- 5 ---- 98 ---- -4
'4040002'------ 2018 ---- 5 ---- 94 ---- 3
...
Account(N-1)------ 2018 ---- 5 ---- x ---- 8
Account(N)------ 2018 ---- 5 ---- x + 8 ---- 11
Where:
98 = 100 + (-2) -> (Transparencia from previous row plus Dif from previous row)
94 = 98 + (-4) -> (Transparencia from previous row plus Dif from previous row)
x = 'Transparencia' from the previos row + 'Dif' from the previous row
x + 8 = 'Transparencia' from the previos row + 8 ('Dif' from the previous row)
The solution I tried was:
select
tmp.Account, tmp.Year_, tmp.Month_,Dif,
case
when Transparencia is null
then (lag(Transparencia, 1, 0) over (order by Account) -
lag(Dif, 1, 0) over (order by Account))
else Transparencia
end Transparencia
from
(select
Account,
nryear as Year_, nrperiod as Month_,
Dif, Transparencia
from
repaca
where
nrperiod = 5) tmp
However, this returns the following result:
Account ------ Year_ ---- Month_ ---- Transparencia ---- Dif
'4030003'------ 2018 ---- 5 ---- 100 ---- -2
'4040001'------ 2018 ---- 5 ---- 98 ---- -4
'4040002'------ 2018 ---- 5 ---- null ---- 3
I need to achieve this using just a SELECT, not a stored procedure or alike. Any help will be appreciated.
Thanks in advance

You don't want lag(). You want cumulative sum. Because the value is NULL, you can simplify getting the first value by using max(). So:
select r.*,
(max(Transparencia) over () +
sum(diff) over (order by year, month)
) as new_Transparencia
from repaca r;
You could also phrase this as:
select r.*,
sum(coalesce(Transparencia, 0) + diff) over (order by year, month) as new_Transparencia
from repaca r;
EDIT:
The above is using the wrong ordering. This seems to be:
select r.*,
(max(Transparencia) over (partition by year, month) +
sum(diff) over (partition by year, month order by account)
) as new_Transparencia
from repaca r;

Related

SQL: converting columns to rows

I currently have a table that looks like this:
date ---- x ---- y ---- z
2020----- 2 ---- 4 ---- 8
2018 ---- 3 ---- 3 ---- 2
2019 ---- 1 ---- 6 ---- 0
I like to rotate this table meaning that the columns become rows like this:
date ---- metric ---- value
2020 ---- x ---- 2
2018 ---- x ---- 3
2019 ---- x ---- 1
2020 ---- y ---- 4
2018 ---- y ---- 3
2019 ---- y ---- 6
2020 ---- z ---- 8
2018 ---- z ---- 2
2019 ---- z ---- 0
If it was in python, I could do it using the pivote() or t() function. However, I am not sure how to do it with SQL. Could you please help me with that?
Thanks!
A canonical method is union all:
select date, 'x' as metric, x as value from t union all
select date, 'y' as metric, y as value from t union all
select date, 'z' as metric, z as value from t;
Some databases support lateral joins, which simplifies this -- and is a bit faster.

Update value in a table in Oracle based upon another table's values

I have two tables and I wish to update a column in the first table (invn_sbs) based upon the results from another table (invn_sbs_qty).
The tables and columns are shown below
Table: invn_sbs
item_sid sbs_no active
-------- ------ ------
12345 6 0
23456 6 0
Table: invn_sbs_qty
item sid sbs_no store_no qty
-------- ------ -------- ---
12345 6 1 5
23456 6 10
What I wish to achieve is to update active = 1 in invn_sbs table
only if qty <> 0 and sbs_no = 6 and store_no = 1 in table invn_sbs_qty.
Therefore only item 12345 would be active = 1 after running the update.
Use the following :
ITEM_SID SBS_NO ACTIVE
-------- ------ ------
12345 6 0
23456 6 0
update invn_sbs s
set active = ( select sign(count(1))
from invn_sbs_qty q
where q.item_sid = s.item_sid
and qty <> 0
and sbs_no = 6
and store_no = 1);
-- results will become :
select * from invn_sbs;
ITEM_SID SBS_NO ACTIVE
-------- ------ ------
12345 6 1
23456 6 0

Show concatenated values in SQL Query

I want to join my first sql query with table Types showed below by id_pr value.ID_PR values are repeated. I want to show all values from ident_st column concatenated with comma separator for same id_pr and rodz_st values. For example: Show all ident_st values for rodz_st='DZE' and id_pr=13.
first query:
SELECT NAME, NO FROM ORDERS o
LEFT JOIN TYPES t ON t.ID_ZM = o.ID_PR
table Orders:
ID ID_ZM NAME NO
---------- ---------- ------- --------
1 12 Dee 333
2 13 Rods 111
table Types:
ID ID_PR RODZ_ST IDENT_ST
---------- ---------- ------- --------
16 12 JEW 646101_1
10 12 JEW 236496_2
11 13 JEW 147301_5
15 13 DZE 259435_1
12 13 OBR 452171_3
13 13 OBR 286432_6
17 12 DZE 618054_1
19 13 DZE 182235_4
I want result like below:
NAME NO JEW DZE OBR
------- ----- ---------------- ------------------ -----------------
Dee 333 646101_1, 236496_2 618054_1
Rods 111 147301_5 259435_1, 182235_4 452171_3, 286432_6
Question: How to create sql join with concatenated statement to get showed result?
You may use list LISTAGG function with DECODE :
SELECT NAME, NO,
LISTAGG(DECODE(RODZ_ST,'JEW',IDENT_ST,NULL), ',') WITHIN GROUP (ORDER BY t.ID DESC, RODZ_ST) AS JEWS,
LISTAGG(DECODE(RODZ_ST,'DZE',IDENT_ST,NULL), ',') WITHIN GROUP (ORDER BY t.ID , RODZ_ST) AS DZE,
LISTAGG(DECODE(RODZ_ST,'OBR',IDENT_ST,NULL), ',') WITHIN GROUP (ORDER BY t.ID , RODZ_ST) AS OBR
FROM ORDERS o
LEFT JOIN TYPES t ON t.ID_PR = o.ID_ZM
GROUP BY NAME, NO;
SQL Fiddle Demo

Complicated pivot in MS SQL Server

I have the following data structure, where value1 and value2 - some aggregated values for each segment and date(I can get unaggregated data, if it helps)
date segment value1 value2
--- ------ ------- ------
What do I need is a report,which looks like this:
2015-01-01 2015-01-02
value1 value2 value1 value2
------ ------ ------ ------
segment1 19 5 18 7
segment2 20 5 21 7
for each date in a given period at the same time. How can I do that?
If I understand the question, you want the segment followed by a sum of the columns Value 1 and value 2, if So here is an easy group to do that:
select segment
, sum(Value1) as Value1
, sum(value2) as Value2
From YourTable
group by segment

Including additional columns with COUNT DISTINCT query

I have a table that has the following columns: Netting_Pool, Counterparty and Account. My goal is to run a SQL query to show when there is a Netting_Pool with more than 1 Counterparty, and to show the Accounts linked to those Counterparties.
An example:
Netting_Pool Counterparty Account
1 ----- A ----- ASD
1 ----- A ----- XYZ
1 ----- B ----- DEF
2 ----- C ----- YUI
3 ----- D ----- TRE
4 ----- E ----- DDW
5 ----- F ----- QWE
I would like the query to have the following Return:
1 ----- A ----- ASD
1 ----- A ----- XYZ
1 ----- B ----- DEF
So far the closest I have come is the following:
SELECT netting_pool, count (distinct counterparty)
FROM Table
GROUP BY netting_pool
HAVING count(distinct counterparty) > 1'
Which returns:
Netting_Pool, Count (distinct Counterparty)
1 2
I have not been able to incorporate the Counterparty or Account values to my query and have it produce the results I want. Any help would be much appreciated!
Your query is aggregating, so you are only going to be getting one row. Another way to do this is with window/analytic functions, which are supported by most but not all databases.
Unfortunately, count(distinct) is not generally supported as a window function. But you can work around this by looking at the maximum and minimum values:
select Netting_Pool, Counterparty, Account
from (select t.*,
min(account) over (partition by Netting_Pool) as mina,
max(account) over (partition by Netting_Pool) as maxa
from table t
) t
where mina <> maxa;