How to expand table based on column value SQL Server [duplicate] - sql

This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 2 years ago.
I have a table looks like this
+----+------+------+-------+
| ID | FY | Code | Value |
+----+------+------+-------+
| 1 | 2021 | A | 2 |
+----+------+------+-------+
| 1 | 2021 | B | 5 |
+----+------+------+-------+
| 1 | 2021 | C | 3 |
+----+------+------+-------+
| 2 | 2021 | A | 4 |
+----+------+------+-------+
| 2 | 2021 | B | 5 |
+----+------+------+-------+
| 2 | 2021 | C | 6 |
+----+------+------+-------+
I want to expand the code column to the following format:
+----+------+---+---+---+
| ID | FY | A | B | C |
+----+------+---+---+---+
| 1 | 2021 | 2 | 5 | 3 |
+----+------+---+---+---+
| 2 | 2021 | 4 | 5 | 6 |
+----+------+---+---+---+
I came up with an ugly way as to use multiple Where sub query and join them together, but there are a few values in 'Code' column which make things ugly.
Is there an elegant way of achieving this? (SQL Server)
Best,

Use conditional aggregation:
select
id,
fy,
max(case when code = 'A' then value end) as A,
max(case when code = 'B' then value end) as B,
max(case when code = 'C' then value end) as C
from mytable
group by id, fy

Related

Query in Oracle between time periods [duplicate]

This question already has answers here:
Dynamic Pivot in Oracle's SQL
(10 answers)
Closed 2 years ago.
I have the following question, when I make a query, how can I return the sums in different columns in various periods of time, for example I have this table:
---------------------------
| PRODUCT | VALUE | MONTH |
---------------------------
| P1 | 10 | 2 |
| P1 | 5 | 3 |
| P1 | 22 | 4 |
| P1 | 4 | 2 |
| P2 | 4 | 2 |
| P2 | 7 | 4 |
| P2 | 10 | 4 |
And I wish I had this result
--------------------------------------
| PRODUCT | MONTH2 | MONTH3 | MONTH4 |
--------------------------------------
| P1 | 14 | 5 | 22 |
| P2 | 4 | 0 | 17 |
You can use conditional aggregation:
select product,
sum(case when month = 2 then value end) as month2,
sum(case when month = 3 then value end) as month3,
sum(case when month = 4 then value end) as month4
from t
group by product;

Create a temp table with multiple conditions

I'm struggling with creating a temporary table with multiple conditions.
Let's call this main table A. I want to pull data from this table to output the distinct account with their last purchase date and payment date to a temporary table.
+---+--------+-----------+----------+
| | Acct | Trans_Date|Trans_code|
+---+--------+-----------+----------+
| 1 | ABC | July 31 | Purchase |
| 2 | ABC | Nov 5 | Payment |
| 3 | DEF | Mar 1 | Purchase |
| 4 | ABC | June 5 | Purchase |
| 5 | GFH | Feb 7 | Payment |
| 6 | GFH | Mar 9 | Purchase |
| 7 | DEF | Aug 8 | Payment |
| 8 | GFH | Mar 9 | Purchase |
| 9 | DEF | Aug 8 | Payment |
+---+--------+---------+----------+
Output result
+---+-------+----------------+--------------+
| | Acct | Last_trans_date|Last_transpay |
+---+-------+----------------+--------------+
| 1 | ABC | July 31 | Nov 5 |
| 2 | DEF | Mar 1 | Aug 8 |
| 3 | GFH | Mar 9 | Feb 7 |
+---+------+-----------------+--------------+
I read that using the WITH clauses could be an option, but struggling to understand it.
You can use conditional aggregation like so:
select acct,
max(case when trans_code = 'Purchase' then trans_date end) as last_purchase,
max(case when trans_code = 'Payment' then trans_date end) as last_payment
from mytable
group by acct
The syntax to insert the result of a query to another table varies across databases. In many of them, you can use:
create table newtable as
select ... -- above query
SQL Server is a notable exception, where you would need:
select ...
into newtable
from ...
group by ...
You can use conditional aggregation:
select acct, max(trans_date),
max(case when trans_code = 'Payment' then trans_date end)
from t
group by acct;
You can then insert this into an existing table or use the appropriate mechanism for your database to save the result as a new table.

Oracle SQL unpivot and keep rows with null values [duplicate]

This question already has an answer here:
oracle - querying NULL values in unpivot query
(1 answer)
Closed 2 years ago.
I'm currently doing an unpivot for a Oracle Data Source (v.12.2) like this:
SELECT *
FROM some_table
UNPIVOT (
(X,Y,Val)
FOR SITE
IN (
(SITE1_X, SITE1_Y, SITE1_VAL) AS '1',
(SITE2_X, SITE2_Y, SITE2_VAL) AS '2',
(SITE3_X, SITE3_Y, SITE3_VAL) AS '3'
))
This works totally fine so far. There is only one exception - I have another column, let's say extend_info, ... if this column has the value y, there will be only one row of this column and all the site columns will be null. Nevertheless I would like to keep this row and not drop it.
I'm not really sure how to do this or what would be a nice way to do this. Any recommendations?
Example:
Original Table:
ID | SITE1_X | SITE1_Y |SITE1_VAL | SITE2_X | SITE2_Y | SITE2_VAL | ... | extend_info
-------
1 | 0 | 0 | 5 | 1 | 1 | 10 | ... | n
2 | 0 | 0 | 3 | null | null | null | ... | n
3 | null | null | null | null | null | null | ... | y
current output:
ID | SITE | X | Y | VAL | extend_info
-------
1 | 1 | 0 | 0 | 5 | n
2 | 1 | 1 | 1 | 10 | n
3 | 2 | 0 | 0 | 3 | n
desired output:
ID | SITE | X | Y | VAL | extend_info
-------
1 | 1 | 0 | 0 | 5 | n
2 | 1 | 1 | 1 | 10 | n
3 | 2 | 0 | 0 | 3 | n
4 | | | | | y
I don't really care what is in SITE|X|Y|VAL in that case, can be 0 for everything or null.
Bonus question:
If extend_info is y I would like to join another table with this ID. The other table looks like this:
ID | F_ID | X | Y | VAL
-----
1 | 4 | 1 | 1 | 8
2 | 4 | 2 | 2 | 9
and in that case my final output table should look like:
ID | SITE | X | Y | VAL | X_OTHER_TABLE | Y_OTHER_TABLE
-------
1 | 1 | 0 | 0 | 5 |
2 | 1 | 1 | 1 | 10 |
3 | 2 | 0 | 0 | 3 |
4 | | | | 8 | 1 | 1
5 | | | | 9 | 2 | 2
I know... the database structure is super ugly but that is what a vendor provides us and we are trying to create a View to make it easier to perform some data analysis tasks on it.
It doesn't have to look 1:1 like my final example - but maybe my itention gets clear = I want to have one single table/view with all the information in a single format.
Thanks for any help!
I would recommend a lateral join:
SELECT s.id, u.*
FROM some_table s CROSS JOIN LATERAL
(SELECT s.SITE1_X as SITE_X, s.SITE1_Y as SITE_Y, s.SITE1_VAL as SITE_VAL FROM DUAL UNION ALL
SELECT s.SITE2_X, s.SITE2_Y, s.SITE2_VAL FROM DUAL UNION ALL
SELECT s.SITE3_X, s.SITE3_Y, s.SITE3_VAL FROM DUAL
) u;
You can just join additional tables to this as you like.

Transpose partially using SQL [duplicate]

This question already has answers here:
SQL Server dynamic PIVOT query?
(9 answers)
Closed 3 years ago.
I would like to do a transposition to create columns from long list.
Here is the example
+-------+--------+--------+-------+
| id | typeid | type | value |
+-------+--------+--------+-------+
| a0001 | 01 | sales | 10 |
| a0001 | 02 | revune | 3 |
| a0001 | 03 | asset | 6 |
| a0002 | 01 | sales | 8 |
| a0002 | 03 | asset | 2 |
| a0003 | 01 | sales | 12 |
| a0003 | 02 | revune | 8 |
| a0003 | 03 | asset | 8 |
+-------+--------+--------+-------+
Since the value in type is enumerable, I would like to transform it into separate columns.
Here is the one I expected:
+-------+-------+---------+-------+
| id | sales | revenue | asset |
+-------+-------+---------+-------+
| a0001 | 10 | 3 | 6 |
| a0002 | 8 | null | 2 |
| a0003 | 12 | 8 | 8 |
+-------+-------+---------+-------+
I know how to do it in py/js.
I would like to know if it is possible to transpose using SQL in the database query?
If you know exactly which columns you want, you can use conditional aggregation:
select id,
sum(case when type = 'sales' then value end) as sales,
sum(case when type = 'revenue' then value end) as revenue,
sum(case when type = 'assets' then value end) as assets
from t
group by id;
If you want this to be flexible, then you need to construct the SQL as a string and execute it. That is called dynamic SQL and depends very much on the database you are using.
You could use conditional aggregation
select a.id
, sum(case when typeid='01' then value else 0 end) sales
, sum(case when typeid='02' then value else 0 end) revenue
, sum(case when typeid='03' then value else 0 end) asset
from my_table
group by id

output difference of two values same column to another column

Can anhone help me out or point me in the right direction? What is simplest way to get from current table to output table??
Current Table
ID | type | amount |
2 | A | 19 |
2 | B | 6 |
3 | A | 5 |
3 | B | 11 |
4 | A | 1 |
4 | B | 23 |
Desires output
ID | type | amount | change |
2 | A | 19 | 13 |
2 | B | 6 | -6 |
3 | A | 5 | -22 |
3 | B | 11 | |
4 | A | 1 | |
4 | B | 23 | |
I don't get how the values are put on rows. You can, for instance, subtract the "B" value from the "A" value for any given id. For instance:
select t.*,
(case when type = 'A'
then amount - max(amount) filter (type = 'B') over (partition by id)
end) as diff_a_b
from t;