Unpivot tax data - sql

I have data in this form:
department_id | VAT_id | Tax_amount | Net_amount | Gross_amount | Date | Invoice_no
1 | 3 | 10 | 90 | 100 | 20130101 | A5
1 | 8 | 5 | 35 | 40 | 20130101 | A5
3 | 3 | 5 | 45 | 50 | 20130101 | A8
And I want to transform it into:
Department_id | Vat_id | Amount | Date | Invoice_No
1 | 3 | 10 | 20130101 | A5
1 | 0 | 90 | 20130101 | A5
1 | -1 | 100 | 20130101 | A5
1 | 8 | 5 | 20130101 | A5
1 | 0 | 35 | 20130101 | A5
1 | -1 | 40 | 20130101 | A5
3 | 3 | 5 | 20130101 | A8
3 | 0 | 45 | 20130101 | A8
3 | -1 | 50 | 20130101 | A8
Vat_id value 0 is for net amount
Vat_id value -1 is for the gross amount.
How can I verticalize this data so that I can keep going forward?

You can use the UNPIVOT function to perform this:
select department_id,
case
when col = 'net_amount' then 0
when col = 'Gross_amount' then -1
else vat_id end vat_od,
amount,
invoice_no
from yourtable
unpivot
(
amount
for col in ([Tax_amount], [Net_amount], [Gross_amount])
) unpiv
See SQL Fiddle with Demo.
If you do not have access to the unpivot function, then you can use a UNION ALL query.
select department_id,
case
when col = 'net_amount' then 0
when col = 'Gross_amount' then -1
else vat_id end vat_od,
amount,
invoice_no
from
(
select department_id, vat_id,
'tax_amount' col, tax_amount amount, invoice_no
from yourtable
union all
select department_id, vat_id,
'Net_amount' col, Net_amount amount, invoice_no
from yourtable
union all
select department_id, vat_id,
'Gross_amount' col, Gross_amount amount, invoice_no
from yourtable
) src
See SQL Fiddle with Demo
Both queries will return:
| DEPARTMENT_ID | VAT_OD | AMOUNT | INVOICE_NO |
------------------------------------------------
| 1 | 3 | 10 | A5 |
| 1 | 0 | 90 | A5 |
| 1 | -1 | 100 | A5 |
| 1 | 8 | 5 | A5 |
| 1 | 0 | 35 | A5 |
| 1 | -1 | 40 | A5 |
| 3 | 3 | 5 | A8 |
| 3 | 0 | 45 | A8 |
| 3 | -1 | 50 | A8 |

Related

Subtract values in each row of a column based on a WHERE and GROUP BY statement in SQL

I would like to subtract each row "Value" with the "Value" where Sub1=0 grouping by ID_1 and ID_2 using a SQL query.
This is the table structure:
------------------------------------
ID_1 |ID_2 | sub1 | Value
------------------------------------
1 | a | 0 | 20
1 | a | 50 | 30
1 | a | 100 | 40
1 | b | 0 | 25
1 | b | 50 | 30
1 | b | 100 | 50
2 | a | 0 | 5
2 | a | 50 | 10
2 | a | 100 | 30
2 | b | 0 | 25
2 | b | 50 | 50
2 | b | 100 | 70
I would like to group by ID_1 and ID_2 and subtract each row's value with the value where the Sub1=0
Output table should be :
------------------------------------
ID_1 |ID_2 | sub1 | Value | Diff
------------------------------------
1 | a | 0 | 20 | 0
1 | a | 50 | 30 | 10
1 | a | 100 | 40 | 20
1 | b | 0 | 25 | 0
1 | b | 50 | 30 | 5
1 | b | 100 | 50 | 25
2 | a | 0 | 5 | 0
2 | a | 50 | 10 | 5
2 | a | 100 | 30 | 25
2 | b | 0 | 25 | 0
2 | b | 50 | 50 | 25
2 | b | 100 | 70 | 45
Use a window function:
select t.*,
(value -
sum(case when sub1 = 0 then value else 0 end) over (partition by id_1, id_2)
) as diff
from t;
This should work:
select t1.*, t1.value - t2.value as diff
from t t1
left join t t2 on t2.id_1 = t1.id_1 and t2.id_2 = t1.id_2 and t2.sub1 = 0
See it here:
http://sqlfiddle.com/#!9/cab4d5/1

SQL - Adding a total line for each distinct value in column 1

I am trying to add a "total count" line for each distinct value coming from my first column. I tired to make a UNION ALL but made a total mess of things. (based on the TL.OP_CODE value.)
Any help GREATLY appreciated. Sorry for my formatting...
select
tl.op_code as "OPS Code", tl.current_status AS "Status", tl.service_level as "Service",
tl.Bill_number as "Bill number", tl.trace_no as "Trace No",
tl.pick_up_by AS "PU By", tl.origname, tl.origcity, tl.origprov,
tl.deliver_by AS "DEL By", tl.destname, tl.destcity, tl.destprov, tl.pallets
from tlorder tl
where tl.current_Status NOT IN ('CANCL')
order by tl.op_code, tl.currency_code, tl.pick_up_by
Try the following approach:
WITH MYTAB (op_code, A, B) AS
(
-- You may put your original SELECT here
-- instead of this artificial VALUES
VALUES
(1, 1, 1)
, (1, 1, 2)
, (2, 1, 1)
)
SELECT op_code, A, B, CAST (NULL AS INT) AS CNT
FROM MYTAB T
UNION ALL
SELECT op_code, NULL, NULL, COUNT (1) CNT
FROM MYTAB
GROUP BY op_code
ORDER BY op_code, CNT DESC;
OP_CODE
A
B
CNT
1
1
1
1
1
2
1
2
2
1
1
2
1
Use Rollup for example if I have student_score table and I am trying to get sum of scores for distinct scores here.
| student_id | score |
| ---------- | ----- |
| 1 | 95 |
| 2 | 95 |
| 3 | 95 |
| 4 | 93 |
| 5 | 93 |
| 6 | 98 |
| 7 | 98 |
| 8 | 99 |
| 9 | 91 |
| 10 | 91 |
SELECT student_id, sum(score) as score from student_score GROUP BY ROLLUP(score, student_id) ORDER BY score;
| student_id | score |
| ---------- | ----- |
| NULL | 948 |
| 9 | 91 |
| 10 | 91 |
| NULL | 182 |
| 4 | 93 |
| 5 | 93 |
| NULL | 186 |
| 1 | 95 |
| 2 | 95 |
| 3 | 95 |
| NULL | 285 |
| 6 | 98 |
| 7 | 98 |
| NULL | 196 |
| 8 | 99 |
| NULL | 99 |

Get all records from Table A and all entries that don't already exist from Table B

I have an Oracle DB with 2 tables, Table A and Table B.
Table A has better data quality but only for a limited set of entries. Table A has also multiple entries (because of history) and I only need the last one per number.
So I need to get all entries from Table A and then the rest of the entries which are not in Table A from Table B.
I also need to get some data from Table B into the result of Table A because the info does not exist in Table A (val1, val2, val3).
So probably some sort of JOIN + GROUP BY?
Table A:
number | valid_from | valid_to | pos1 | pos2 | pos3 | factor | loc
100 | 2020-03-01 | 2020-03-10 | 7 | 80 | 18 | 19 | 1
100 | 2020-03-10 | 2020-03-13 | 7 | 80 | 18 | 19 | 1
100 | 2020-03-13 | 2020-03-16 | 8 | 80 | 18 | 20 | 1
200 | 2020-03-02 | 2020-03-03 | 6 | 90 | 19 | 30 | 1
200 | 2020-03-03 | 2020-03-04 | 6 | 90 | 19 | 29 | 1
200 | 2020-03-04 | 2020-03-10 | 9 | 90 | 19 | 30 | 1
300 | 2020-03-10 | 2020-03-12 | 13 | 100 | 10 | 41 | 2
300 | 2020-03-12 | 2020-03-14 | 13 | 100 | 10 | 40 | 2
300 | 2020-03-14 | 2020-03-20 | 10 | 100 | 10 | 40 | 2
Table B:
number | pos1 | pos2 | pos3 | val1 | val2 | val3 | top
100 | 7 | 70 | 18 | a | aa | aaa | 3
200 | 6 | 60 | 19 | b | bb | bbb | 4
300 | 5 | 50 | 10 | c | cc | ccc | 5
400 | 2 | 20 | 2 | d | dd | ddd | 16
500 | 3 | 30 | 3 | e | ee | eee | 28
End result should be:
number | pos1 | pos2 | pos3 | factor | loc | val1 | val2 | val3 | top
100 | 8 | 80 | 18 | 20 | 1 | a | aa | aaa | 3
200 | 9 | 90 | 19 | 30 | 1 | b | bb | bbb | 4
300 | 10 | 100 | 10 | 40 | 2 | c | cc | ccc | 5
400 | 2 | 20 | 2 | NULL | NULL | d | dd | ddd | 16
500 | 3 | 30 | 3 | NULL | NULL | e | ee | eee | 28
How can I achieve this? Do I need a FULL LEFT JOIN and GROUP BY by number? Not sure what to take or how to get the latest entries from Table A.
select b.number, b.pos1, b.pos2, b.pos3,
a.factor, a.loc, b.val1, b.val2, b.val3, b.top
from tableb b
left outer join tablea a
on b.number = a.number
you can also use NVL(b.pos1, a.pos1) which means if b.pos1 is null take a.pos1
I would write this as a left join with filtering:
select b.number, b.pos1, b.pos2, b.pos3,
a.factor, a.loc,
b.val1, b.val2, b.val3, b.top
from b
left join (
select a.*, row_number() over(partition by number order by valid_from desc) rn
from a
) a on a.number = b.number and a.rn = 1

How to get second largest column value and column name

How can I get second largest column value and its name?
My current query gives it mostly correct but in cases where largest and second largest values are same I am getting wrong values.
select item_code, A, B, C,
greatest(A, B, C) as largest1,
greatest(case when largest1 = A then 0 else A end,
case when largest1 = B then 0 else B end,
case when largest1 = C then 0 else C end) as largest2,
(case largest1 when A then 'A'
when B then 'B'
when C then 'C' end) as largest1_column_name,
(case largest2 when A then 'A'
when B then 'B'
when C then 'C' else 'None' end) as largest2_column_name
from table1
Below is the sample table:
+-----------+----+----+----+
| item_code | A | B | C |
+-----------+----+----+----+
| p1 | 20 | 30 | 40 |
| p2 | 50 | 30 | 10 |
| p3 | 30 | 50 | 10 |
| p4 | 30 | 30 | 30 |
| p5 | 50 | 50 | 10 |
| p6 | 0 | 0 | 0 |
+-----------+----+----+----+
Below is expected output:
+-----------+----+----+----+----------+----------+----------------------+----------------------+
| item_code | A | B | C | largest1 | largest2 | largest1_column_name | largest2_column_name |
+-----------+----+----+----+----------+----------+----------------------+----------------------+
| p1 | 20 | 30 | 40 | 40 | 30 | C | B |
| p2 | 50 | 30 | 10 | 50 | 30 | A | B |
| p3 | 30 | 50 | 10 | 50 | 30 | B | A |
| p4 | 30 | 30 | 30 | 30 | 30 | A | B |
| p5 | 50 | 50 | 10 | 50 | 50 | A | B |
| p6 | 0 | 0 | 0 | 0 | 0 | A | B |
+-----------+----+----+----+----------+----------+----------------------+----------------------+
This is the output I am getting from my query (I have marked wrong as comment):
+-----------+----+----+----+----------+-------------+----------------------+----------------------+
| item_code | A | B | C | largest1 | largest2 | largest1_column_name | largest2_column_name |
+-----------+----+----+----+----------+-------------+----------------------+----------------------+
| p1 | 20 | 30 | 40 | 40 | 30 | C | B |
| p2 | 50 | 30 | 10 | 50 | 30 | A | B |
| p3 | 30 | 50 | 10 | 50 | 30 | B | A |
| p4 | 30 | 30 | 30 | 30 | 0/*wrong*/ | A | NULL/*wrong*/ |
| p5 | 50 | 50 | 10 | 50 | 10/*wrong*/ | A | C/*wrong*/ |
| p6 | 0 | 0 | 0 | 0 | 0/*wrong*/ | A | A/*wrong*/ |
+-----------+----+----+----+----------+-------------+----------------------+----------------------+
I tried a slight variation of this (listagg instead of string_agg) in Snowflake and it seemed to be getting the expected result
with cte (item_code, abc, id) as
(select item_code, a, 'a' from table1 union all
select item_code, b, 'b' from table1 union all
select item_code, c, 'c' from table1)
select item_code,
max(case when id='a' then abc end) a,
max(case when id='b' then abc end) b,
max(case when id='c' then abc end) c,
split_part(string_agg(abc::varchar,',' order by abc desc),',',1) largest1,
split_part(string_agg(abc::varchar,',' order by abc desc),',',2) largest2,
split_part(string_agg(id,',' order by abc desc),',',1) largest1_col,
split_part(string_agg(id,',' order by abc desc),',',2) largest2_col
from cte
group by item_code;
This might be simpler achieved by unpivoting the rows, ranking the values, and then using conditional aggregation. In Postgres, you could phrase this as:
select t.*, x.*
from table1 t1
cross join lateral (
select
min(val) filter(where rn = 1) largest1,
min(val) filter(where rn = 2) largest2,
min(col) filter(where rn = 1) largest1_column_name,
min(col) filter(where rn = 2) largest2_column_name
from (
select x.*, dense_rank() over(order by val desc) rn
from (values ('a', a), ('b', b), ('c', c)) as x(col, val)
) x
) x

Oracle SQL: getting all row maximum number from specific multiple criteria

I have the following table named foo:
ID | D1 | D2 | D3 |
---------------------
1 | 47 | 3 | 71 |
2 | 47 | 98 | 82 |
3 | 0 | 99 | 3 |
4 | 3 | 100 | 6 |
5 | 48 | 10 | 3 |
6 | 49 | 12 | 4 |
I want to run a select query and have the results show like this
ID | D1 | D2 | D3 | Result |
------------------------------
1 | 47 | 3 | 71 | D3 |
2 | 47 | 98 | 82 | D2 |
3 | 0 | 99 | 3 | D2 |
4 | 3 | 100 | 6 | D2 |
5 | 48 | 10 | 3 | D1 |
6 | 49 | 12 | 4 | D1 |
So, basically I want to get Maximum value between D1, D2, D3 column divided by id.
As You may seen , ID 1 have D3 in the Result column since Maximum value between
D1 : D2 : D3
That Means 4 : 3 : 71 , Max value is 71. Thats Why The Result show 'D3'
Is there a way to do this in a sql query ?
Thanks!
For Oracle please try this one
select foo.*, case when greatest(d1, d2, d3) = d1 then 'D1'
when greatest(d1, d2, d3) = d2 then 'D2'
when greatest(d1, d2, d3) = d3 then 'D3'
end result
from foo
Consider the following - a normalized approach...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL
,d INT NOT NULL
,val INT NOT NULL
,PRIMARY KEY(id,d)
);
INSERT INTO my_table VALUES
(1,1,47),
(2,1,47),
(3,1,0),
(4,1,3),
(5,1,48),
(6,1,49),
(1,2,3),
(2,2,98),
(3,2,99),
(4,2,100),
(5,2,10),
(6,2,12),
(1,3,71),
(2,3,82),
(3,3,3),
(4,3,6),
(5,3,3),
(6,3,4);
SELECT * FROM my_table;
+----+---+-----+
| id | d | val |
+----+---+-----+
| 1 | 1 | 47 |
| 1 | 2 | 3 |
| 1 | 3 | 71 |
| 2 | 1 | 47 |
| 2 | 2 | 98 |
| 2 | 3 | 82 |
| 3 | 1 | 0 |
| 3 | 2 | 99 |
| 3 | 3 | 3 |
| 4 | 1 | 3 |
| 4 | 2 | 100 |
| 4 | 3 | 6 |
| 5 | 1 | 48 |
| 5 | 2 | 10 |
| 5 | 3 | 3 |
| 6 | 1 | 49 |
| 6 | 2 | 12 |
| 6 | 3 | 4 |
+----+---+-----+
SELECT x.*
FROM my_table x
JOIN
( SELECT id,MAX(val) max_val FROM my_table GROUP BY id) y
ON y.id = x.id
AND y.max_val = x.val;
+----+---+-----+
| id | d | val |
+----+---+-----+
| 1 | 3 | 71 |
| 2 | 2 | 98 |
| 3 | 2 | 99 |
| 4 | 2 | 100 |
| 5 | 1 | 48 |
| 6 | 1 | 49 |
+----+---+-----+
(This is intended as a MySQL solution - I'm not familiar with ORACLE syntax, so apologies if this doesn't port)
Does this answer your comment?
SELECT x.* , y.max_val
FROM my_table x
JOIN
( SELECT id,MAX(val) max_val FROM my_table GROUP BY id) y
ON y.id = x.id ;
+----+---+-----+---------+
| id | d | val | max_val |
+----+---+-----+---------+
| 1 | 1 | 47 | 71 |
| 1 | 2 | 3 | 71 |
| 1 | 3 | 71 | 71 |
| 2 | 1 | 47 | 98 |
| 2 | 2 | 98 | 98 |
| 2 | 3 | 82 | 98 |
| 3 | 1 | 0 | 99 |
| 3 | 2 | 99 | 99 |
| 3 | 3 | 3 | 99 |
| 4 | 1 | 3 | 100 |
| 4 | 2 | 100 | 100 |
| 4 | 3 | 6 | 100 |
| 5 | 1 | 48 | 48 |
| 5 | 2 | 10 | 48 |
| 5 | 3 | 3 | 48 |
| 6 | 1 | 49 | 49 |
| 6 | 2 | 12 | 49 |
| 6 | 3 | 4 | 49 |
+----+---+-----+---------+