SQL: converting columns to rows - sql

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.

Related

Generate frequency table from sql using Count with user defined condition

Basically I need to generate a frequency table using sql, and I have a sample table like this:
user_id user_label code1 date
------ ----------- ----- ------
1 x a 01-01
1 x a 01-01
1 x a 01-02
1 x b 01-01
1 x c 01-02
1 y a 01-01
2 x a 01-01
etc
The rule to count occurrences is if two rows have the same user_id ,user_label and date ,then repeated codes should only be counted once.
For example, for the first two rows the frequency table should be :
user_id user_label code1 count_code_1
-------- ----------- ----- ------------
1 x a 1
Because even though there are two instances of a, but they happen on the same date so should only be counted once and I need do this for every unique codes in code_1 column
for all combinations of user_id + user_label
After processing the third row , the frequency table should be :
user_id user_label code_1 count_code_1
-------- ----------- ------ ------------
1 x a 2
Since although is the same code ('a') but it happens on a different date (01-02)
In the end, for the sample table given above, the desired result should be
user_id user_label code_1 count_code_1
-------- ----------- ------ -------------
1 x a 2
1 x b 1
1 x c 1
1 y a 1
2 x a 1
What I have so far is
select t.user_id, t.user_label, t.code_1, count(###)
from t
group by t.code_1,t.user_id, t.user_label
The problem is
1. I don't know what to put inside the count 2. I don't know how to incorporate the condition on date in to this query.
Any suggestion, correction would be greatly appreciated.
You seem to want count(distinct date):
select t.user_id, t.user_label, t.code_1,
count(distinct date)
from t
group by t.code_1,t.user_id, t.user_label

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

Use of LAG - SQL Server 2014

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;

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;