Unpivoting in db2 - sql

I have the below table
+------+------+---+---+---+
| type | year | a | b | c |
+------+------+---+---+---+
| x | 2015 | 1 | 1 | 1 |
| x | 2015 | 2 | 2 | 2 |
| x | 2017 | 3 | 3 | 3 |
| y | 2016 | 1 | 1 | 1 |
| y | 2017 | 2 | 2 | 2 |
| z | 2015 | 1 | 1 | 1 |
| z | 2016 | 3 | 3 | 3 |
+------+------+---+---+---+
The expected result must be as follows
+------+------+---+---+---+
| type | year | x | y | z |
+------+------+---+---+---+
| a | 2015 | 3 | 0 | 1 |
| a | 2016 | 0 | 1 | 3 |
| a | 2017 | 3 | 2 | 0 |
| b | 2015 | 3 | 2 | 0 |
| b | 2016 | 0 | 1 | 3 |
| b | 2017 | 3 | 2 | 0 |
| c | 2015 | 3 | 0 | 1 |
| c | 2016 | 0 | 1 | 3 |
| c | 2017 | 3 | 2 | 0 |
+------+------+---+---+---+
So far, I could write the following query to obtain the result using simple group by in a pivot table manner, but i need to display the result in the unpivot manner as my expected result shown as above.
select type, year, sum(a) as a, sum(b) as b, sum(c) as c from table group by type,year;
The result of the above query is a valuable result for me but in a different format
+------+------+---+---+---+
| type | year | a | b | c |
+------+------+---+---+---+
| x | 2015 | 3 | 3 | 3 |
| x | 2017 | 3 | 3 | 3 |
| y | 2016 | 1 | 1 | 1 |
| y | 2017 | 2 | 2 | 2 |
| z | 2015 | 1 | 1 | 1 |
| z | 2016 | 3 | 3 | 3 |
+------+------+---+---+---+

You can unpivot using union all and then reaggregate:
select col_type, year,
sum(case when type = 'x' then val end) as x,
sum(case when type = 'y' then val end) as y,
sum(case when type = 'z' then val end) as z
from (select type, year, 'a' as col_type, a as val from t union all
select type, year, 'b' as col_type, b as val from t union all
select type, year, 'c' as col_type, c as val from t
) x
group by col_type, year;

Related

How to assign duplicate increment in SQL?

While going through SQL columns, if we find text match "NEW" in Calc column, update the incrementing a count starting with 1 in Results column.
It should look like this on the output:
The following uses an id column to resolve the order issue. Replace that with your corresponding expression. This also addresses the requirement to start the display sequence with 1 and also show 0 for the 'NEW' rows.
The SQL (updated):
SELECT logs.*
, CASE WHEN text = 'NEW' THEN 0
ELSE
COALESCE(SUM(CASE WHEN text = 'NEW' THEN 1 END) OVER (PARTITION BY xrank ORDER BY id)+1, 1)
END AS display
FROM logs
ORDER BY id
The result:
+----+-------+------+---------+
| id | xrank | text | display |
+----+-------+------+---------+
| 1 | 1 | A | 1 |
| 2 | 1 | B | 1 |
| 3 | 1 | C | 1 |
| 4 | 1 | NEW | 0 |
| 5 | 1 | D | 2 |
| 6 | 1 | Q | 2 |
| 7 | 1 | B | 2 |
| 8 | 1 | NEW | 0 |
| 9 | 1 | D | 3 |
| 10 | 1 | Z | 3 |
| 11 | 2 | A | 1 |
| 12 | 2 | B | 1 |
| 13 | 2 | C | 1 |
| 14 | 2 | NEW | 0 |
| 15 | 2 | D | 2 |
| 16 | 2 | Q | 2 |
| 17 | 2 | B | 2 |
| 18 | 2 | NEW | 0 |
| 19 | 2 | D | 3 |
| 20 | 2 | Z | 3 |
+----+-------+------+---------+
You need a column that specifies the ordering for the table. With that, just use a cumulative sum:
select t.*,
1 + sum(case when Calc = 'NEW' then 1 else 0 end) over (partition by Rank_Id order by Seq) as display
from t;

Query with WITH clause and COUNT subquery

In the query below, I don't get the results i would expect. Any insights why? How could i reformulate such query to get the desired results?
Schema (SQLite v3.30)
WITH RECURSIVE
cnt(x,y) AS (VALUES(0,ABS(Random()%3)) UNION ALL SELECT x+1, ABS(Random()%3) FROM cnt WHERE x<10),
i_rnd as (SELECT r1.x, r1.y, (SELECT COUNT(*) FROM cnt as r2 WHERE r2.y<=r1.y) as idx FROM cnt as r1)
SELECT * FROM i_rnd ORDER BY y;
result:
| x | y | idx |
| --- | --- | --- |
| 1 | 0 | 3 |
| 5 | 0 | 6 |
| 8 | 0 | 5 |
| 9 | 0 | 4 |
| 10 | 0 | 2 |
| 3 | 1 | 4 |
| 0 | 2 | 11 |
| 2 | 2 | 11 |
| 4 | 2 | 11 |
| 6 | 2 | 11 |
| 7 | 2 | 11 |
expected result:
| x | y | idx |
| --- | --- | --- |
| 1 | 0 | 5 |
| 5 | 0 | 5 |
| 8 | 0 | 5 |
| 9 | 0 | 5 |
| 10 | 0 | 5 |
| 3 | 1 | 6 |
| 0 | 2 | 11 |
| 2 | 2 | 11 |
| 4 | 2 | 11 |
| 6 | 2 | 11 |
| 7 | 2 | 11 |
In other words, idx should indicate how many rows have y less or equal than the y of row considered.
I would just use:
select cnt.*,
count(*) over (order by y)
from cnt;
Here is a db<>fiddle.
The issue with your code is probably that the CTE is re-evaluated each time it is called, so the values are not consistent -- a problem with volatile functions in CTEs.

SQL Sum divided weight over adjoining months

I have the following table (Sample):
+----+--------+-------+------+------------+
| ID | WEIGHT | MONTH | YEAR | CATEGORYID |
+----+--------+-------+------+------------+
| 1 | 0.5 | 1 | 2014 | A |
| 1 | 0.5 | 1 | 2014 | A |
| 1 | 0.5 | 2 | 2014 | A |
| 1 | 0.2 | 2 | 2014 | C |
| 1 | 0.2 | 2 | 2014 | C |
| 2 | 1.0 | 2 | 2014 | B |
| 2 | 1.0 | 2 | 2014 | B |
+----+--------+-------+------+------------+
The Output I want would be like this (Sample):
+----+--------+-------+------+------------+
| ID | WEIGHT | MONTH | YEAR | CATEGORYID |
+----+--------+-------+------+------------+
| 1 | 1.5 | 1 | 2014 | A |
| 1 | 1.5 | 2 | 2014 | A |
| 1 | 0.4 | 1 | 2014 | C |
| 1 | 0.4 | 2 | 2014 | C |
| 2 | 2.0 | 2 | 2014 | B |
| 2 | 2.0 | 3 | 2014 | B |
+----+--------+-------+------+------------+
So, when the month breaks I still want to sum the weight from previous month into the current etc. I want to sum the weight on the specific ID & CategoryID.
Hope this works.
select DISTINCT ID,sum(WEIGHT) over (partition by categoryid order by categoryid) as WEIGHT,
MONTH,YEAR, CATEGORYID
from table;
Try this
SELECT ID,
Sum(s_weight)OVER(partition BY CATEGORYID, id),
MONTH,
YEAR,
CATEGORYID
FROM (SELECT ID,
Sum(weight) AS s_weight,
MONTH,
YEAR,
CATEGORYID
FROM Yourtable
GROUP BY ID,
MONTH,
YEAR,
CATEGORYID) a

Advanced SQL SUM COLUMN FROM A SAME TABLE

I have this table...
+-----+--------+------+-----+-----+
|categ| nAME | quan |IDUNQ| ID|
+-----+--------+------+-----+-----+
| 1 | Z | 3 | 1 | 15 |
| 1 | A | 3 | 2 | 16 |
| 1 | B | 3 | 3 | 17 |
| 2 | Z | 2 | 4 | 15 |
| 2 | A | 2 | 5 | 16 |
| 3 | Z | 1 | 6 | 15 |
| 3 | B | 1 | 7 | 17 |
| 2 | Z | 1 | 8 | 15 |
| 2 | C | 4 | 8 | 15 |
| 1 | D | 1 | 8 | 15 |
+-----+--------+------+-----+-----+
I need to get the Z of category 1 + Z of category 2 - Z of category 3
For example, (3+3-1) = 5 ==> 3 of cat 1, 3 of cat 2, 1 of cat 3
The final result should be...
Z ==> 5
A ==> 5
B ==> 2
C ==> 4
Note: I'm assuming the data for "C" from your example was mistakenly omitted.
SELECT nAME, SUM(CASE categ WHEN 3 THEN 0-quan ELSE quan END) AS quan
FROM theTable
GROUP BY nAME
SQL Fiddle
SELECT name, SUM(quan) AS sum
FROM tableName
GROUP BY name, categ
This should work.

Crosstab multi columns

Hello I have a problem with SQL in SQL Server 2005.
Suppose that I have a table called myTable with data as below:
| NAME | CREDIT | GRADE | YEAR | SEMESTER |
---------------------------------------------
| Name1 | 1 | A | 1 | 1 |
| Name2 | 4 | B | 1 | 1 |
| Name3 | 2 | E | 1 | 1 |
| Name4 | 7 | F | 1 | 1 |
| Name5 | 4 | A | 1 | 2 |
| Name6 | 3 | C | 1 | 2 |
| Name7 | 6 | D | 1 | 2 |
| Name8 | 1 | A | 1 | 2 |
| Name9 | 1 | A | 1 | 2 |
| Name10 | 1 | A | 1 | 2 |
| Name11 | 3 | C | 2 | 1 |
| Name12 | 6 | E | 2 | 1 |
| Name13 | 4 | C | 2 | 1 |
| Name14 | 2 | B | 2 | 2 |
| Name15 | 1 | A | 2 | 2 |
| Name16 | 1 | A | 2 | 2 |
| Name17 | 1 | A | 2 | 2 |
| Name18 | 5 | D | 3 | 1 |
| Name19 | 1 | A | 3 | 1 |
| Name20 | 1 | A | 3 | 1 |
| Name18 | 5 | D | 3 | 2 |
| Name19 | 1 | A | 3 | 2 |
| Name20 | 1 | A | 3 | 2 |
I want to output the result as below:
| NAM1 | CRDT1 | GRD1 | YEAR1 | SEMER1 | NAM2 | CRDT2 | GRD2 | YEAR2 | SEMES2 |
-----------------------------------------------------------------------------
| Name1| 1 | A | 1 | 1 |Name5 | 4 | A | 1 | 2 |
| Name2| 4 | B | 1 | 1 |Name6 | 3 | C | 1 | 2 |
| Name3| 2 | E | 1 | 1 |Name7 | 6 | D | 1 | 2 |
| Name4| 7 | F | 1 | 1 |Name8 | 1 | A | 1 | 2 |
|Name9 | 1 | A | 1 | 2 |
|Name10| 1 | A | 1 | 2 |
| Name11| 3 | C | 2 | 1 |Name14| 2 | B | 2 | 2 |
| Name12| 6 | E | 2 | 1 |Name15| 1 | A | 2 | 2 |
| Name13| 4 | C | 2 | 1 |Name16| 1 | A | 2 | 2 |
|Name17| 1 | A | 2 | 2 |
| Name18| 5 | D | 3 | 1 |Name18| 5 | D | 3 | 2 |
| Name19| 1 | A | 3 | 1 |Name19| 1 | A | 3 | 2 |
| Name20| 1 | A | 3 | 1 |Name20| 1 | A | 3 | 2 |
Where
- Nam1= Name in Semester 1
- CRDT1= Credit in Semester 1
- GRD1= Grade in Semester 1
- Year1= Year in Semester 1
- Semer1 = Semester in Semester 1
- Nam2= Name in Semester 2
- CRDT2= Credit in Semester 2
- GRD2= Grade in Semester 2
- Year2= Year in Semester 2
- Semer2 = Semester in Semester 2
Please go to this URL to test this SQL: http://sqlfiddle.com/#!3/196c6/1
How Can I create SQL to make output like this?
select
s1.Name as nam1, s1.credit as crdt1, s1.Year as year1, s1.semester as semer1,
s2.Name as nam2, s2.credit as crdt2, s2.Year as year2, s2.semester as semer2
from
(select *, ROW_NUMBER() over (partition by year order by name) rn from myTable where semester=1 ) s1
full outer join
(select *, ROW_NUMBER() over (partition by year order by name) rn from myTable where semester=2 ) s2
on s1.year = s2.year
and s1.rn = s2.rn
I don't like doing an outer join, when a simple group by is sufficient:
select max(case when semester = 1 then Name end) as name1,
max(case when semester = 1 then credit end) as credit1,
max(case when semester = 1 then year end) as year1,
max(case when semester = 1 then semester end) as semester1,
max(case when semester = 2 then Name end) as name2,
max(case when semester = 2 then credit end) as credit2,
max(case when semester = 2 then year end) as year2,
max(case when semester = 2 then semester end) as semester2
from (select t.*,
row_number() over (partition by semester order by name) as rownum
from t
) t2
group by rownum
order by rownum
select Name,credit, grade, year,semester from myTable
group by semester,year, Name,credit, grade;
now we have to make a dynamic query with this previous query:
create as temporary table as there are semster first
create dynamically a select query with all fields of all semester table in a loop:
foreach temporary table concat all fields of this table in select query
and add construct label field with semester value of this table
and add temporary table with union
'select' + #tbls1.fieldName + ',' + ... + + #tbls2.fieldName +