SQL row values to columns - sql

I need help with something similar to PIVOT, but it isn't that easy.
I have table with this structure, and i need to get structure something like in the bottom of the picture:
Can anyone help me?
Thank you guys!

Here is a solution using a pivot:
with testdata as
(select 'first' casekey, 1 custom_field_id, 'value1' custom_field_value from dual union all
select 'first' casekey, 2 custom_field_id, 'value2' custom_field_value from dual union all
select 'first' casekey, 3 custom_field_id, 'value3' custom_field_value from dual union all
select 'second' casekey, 6 custom_field_id, 'value1' custom_field_value from dual union all
select 'second' casekey, 9 custom_field_id, 'value1' custom_field_value from dual)
select *
from(
select casekey, custom_field_id, custom_field_value,
rank() over(partition by casekey order by custom_field_id) pivotRank
from testdata)
pivot(
max(custom_field_id || ':' || custom_field_value)
for pivotRank in (1 as CF1, 2 as CF2, 3 as CF3, 4 as CF4, 5 as CF5)
)
First I use a windowing function to rank the custom_field_id column partitioned by casekey. Then all you have to do is take the max of the concatenated fields you wanted and pivot 1 through 5 on the pivotRank.
My output for the above query looks like this:
casekey CF1 CF2 CF3 CF4 CF5
first 1:value1 2:value2 3:value3 (null) (null)
second 6:value1 9:value1 (null) (null) (null)

try this:
with y as (
select cas, cfi || ':' || cfv cf, ROW_NUMBER() over (partition by cas order by cfi) n
from x)
, z AS (
select DISTINCT cas
FROM x
)
select z.cas, cf1.cf, cf2.cf, cf3.cf, cf4.cf, cf5.cf
from z
left join y as cf1 on z.cas = cf1.cas and cf1.n = 1
left join y as cf2 on z.cas = cf2.cas and cf1.n = 2
left join y as cf3 on z.cas = cf3.cas and cf1.n = 3
left join y as cf4 on z.cas = cf4.cas and cf1.n = 4
left join y as cf5 on z.cas = cf5.cas and cf1.n = 5

Related

how to get distinct values from multiple columns in 1 single row in oracle sql

I have a row of data like this
id first_cd sec_cd third_cd fourth_cd fifth_cd sixth_cd
1 A B null C C D
output should be :
id first_cd sec_cd third_cd fourth_cd fifth_cd sixth_cd
1 A B C D D D
I need to get distinct values from the columns and remove nulls where there are.
if, first_cd...sixth_cd are columns on the same row.
1 A B null C C D are the values
Anyway to do in this in oracle sql
This is a good place to use lateral joins:
select t.*, x.*
from t cross join lateral
(select max(case when seqnum = 1 then cd end) as cd1,
max(case when seqnum = 2 then cd end) as cd2,
max(case when seqnum = 3 then cd end) as cd3,
max(case when seqnum = 4 then cd end) as cd4,
max(case when seqnum = 5 then cd end) as cd5,
max(case when seqnum = 6 then cd end) as cd6
from (select t.*, row_number() over (order by n) as seqnum
from (select t.cd1 as cd, 1 as n from dual union all
select t.cd2, 2 from dual union all
select t.cd3, 3 from dual union all
select t.cd4, 4 from dual union all
select t.cd5, 5 from dual union all
select t.cd6, 6 from dual
) x
where cd is not null
) x
) x;
Note: This returns the excess values as NULL, which seems more in line with your problem.

How put grouping variable to columns in SQL/

I have following dataset
and want to get this
How can I do it?
Using SQL Server, you can use a PIVOT, such as :
SELECT Time, [a],[b],[c]
FROM
(
SELECT time, [group],value
FROM dataset) d
PIVOT
(
SUM(value)
FOR [group] IN ([a],[b],[c])
) AS pvt
You can try it on the following fiddle.
Changed the column names to not conflict with reserved words. You would have to put them into single quotes otherwise.
WITH
-- the input
indata(grp,tm,val) AS (
SELECT 'a',1,44
UNION ALL SELECT 'a',2,22
UNION ALL SELECT 'a',3, 1
UNION ALL SELECT 'b',1, 1
UNION ALL SELECT 'b',2, 5
UNION ALL SELECT 'b',3, 6
UNION ALL SELECT 'c',1, 7
UNION ALL SELECT 'c',2, 8
UNION ALL SELECT 'c',3, 9
)
SELECT tm
, SUM(CASE grp WHEN 'a' THEN val END) AS a
, SUM(CASE grp WHEN 'b' THEN val END) AS b
, SUM(CASE grp WHEN 'c' THEN val END) AS c
FROM indata
GROUP BY tm
;
tm | a | b | c
----+----+---+---
1 | 44 | 1 | 7
2 | 22 | 5 | 8
3 | 1 | 6 | 9
select * from
(
select
time,[group],value
from yourTable
group by time,[group],value
)
as table
pivot
(
sum([value])
for [group] in ([a],[b],[c])
) as p
order by time
This is the result
for Vertica,
SELECT time
, SUM(value) FILTER (WHERE group = a) a
, SUM(value) FILTER (WHERE group = b) b
, SUM(value) FILTER (WHERE group = c) c
FROM yourTable
GROUP BY time

T-SQL Issue with joins

I am trying to achieve the Expected Result which is at the end of my post from two data sets. I am using left join which is not helping me to get my expected result. I would like to get all the matched and unmatched records from both tables.
Query used to create and retrieve dataset and my current result set are below:
select
a.id, a.name, a.rev, isnull(b.conv, 0) as conv
from
(select
1 as id, 'A' as name, 2 as rev, 0 as conv
union all
select
2 as id, 'B' as name, 1 as rev, 0 as conv) a
left join
(select
1 as id, 'A' as name, 0 AS rev, 2 as conv
union all
select
3 as id, 'C' as name, 0 as rev, 3 as conv) b on a.id = b.id
Current result from above query using left join:
id name rev conv
---------------------
1 A 2 2
2 B 1 0
Expected result:
id name rev conv
--------------------
1 A 2 2
2 B 1 0
3 C 0 3
Use FULL JOIN instead:
SELECT ISNULL(a.id,b.id) id,
ISNULL(a.name,b.name) name,
ISNULL(a.rev,0) rev,
ISNULL(b.conv,0) conv
FROM ( SELECT 1 as id , 'A' as name , 2 as rev, 0 as conv
UNION ALL
SELECT 2 as id , 'B' as name, 1 as rev, 0 as conv) a
FULL JOIN ( SELECT 1 as id , 'A' as name, 0 AS rev, 2 as conv
UNION ALL
SELECT 3 as id , 'C' as name, 0 as rev, 3 as conv) b
ON a.id = b.id
ORDER BY ISNULL(a.id,b.id);
You should try with FULL OUTER JOIN, this combines the results of both LEFT and RIGHT joins.
Sintax:
SELECT column_name(s)
FROM
table1 FULL OUTER JOIN table2
ON
table1.column_name = table2.column_name;
I hope it helps!

How can I get the first result for each account in this SQL query?

I'm trying to write a query that follows this logic:
Find the first following status code of an account that had a previous status code of X.
So if I have a table of:
id account_num status_code
64 1 X
82 1 Y
72 2 Y
87 1 Z
91 2 X
103 2 Z
The results would be:
id account_num status_code
82 1 Y
103 2 Z
I've come up with a couple of solutions but I'm not all that great with SQL and so they've been pretty inelegeant thus far. I was hoping that someone here might be able to point me in the right direction.
View:
SELECT account_number, id
FROM table
WHERE status_code = 'X'
Query:
SELECT account_number, min(id)
FROM table
INNER JOIN view
ON table.account_number = view.account_number
WHERE table.id > view.id
At this point I have the id that I need but I'd have to write ANOTHER query that uses the id tolook up the status_code.
Edit: To add some context, I'm trying to find calls that have a status_code of X. If a call has a status_code of X we want to dial it a different way the next time we make an attempt. The aim of this query is to provide a report that will show the results of the second dial if the first dial resulted an X status code.
Here's a SQL Server solution.
UPDATE
The idea is to avoid a number of NESTED LOOP joins as proposed by Olaf because they roughly have O(N * M) complexity and thus extremely bad for your performance. MERGED JOINS complexity is O(NLog(N) + MLog(M)) which is much better for real world scenarios.
The query below works as follows:
RankedCTE is a subquery that assigns a row number to each id partioned by account and sorted by id which represents the time. So for the data below the output of this
SELECT
id,
account_num,
status_code,
ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
FROM dbo.Test
would be:
id account_num status_code item_rank
----------- ----------- ----------- ----------
87 1 Z 1
82 1 Y 2
64 1 X 3
103 2 Z 1
91 2 X 2
72 2 Y 3
Once we have them numbered we join the result on itself like this:
WITH RankedCTE AS
(
SELECT
id,
account_num,
status_code,
ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
FROM dbo.Test
)
SELECT
*
FROM
RankedCTE A
INNER JOIN RankedCTE B ON
A.account_num = B.account_num
AND A.item_rank = B.item_rank - 1
which will give us an event and a preceding event in the same table
id account_num status_code item_rank id account_num status_code item_rank
----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
87 1 Z 1 82 1 Y 2
82 1 Y 2 64 1 X 3
103 2 Z 1 91 2 X 2
91 2 X 2 72 2 Y 3
Finally, we just have to take the preceding event with code "X" and the event with code not "X":
WITH RankedCTE AS
(
SELECT
id,
account_num,
status_code,
ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
FROM dbo.Test
)
SELECT
A.id,
A.account_num,
A.status_code
FROM
RankedCTE A
INNER JOIN RankedCTE B ON
A.account_num = B.account_num
AND A.item_rank = B.item_rank - 1
AND A.status_code <> 'X'
AND B.status_code = 'X'
Query plans for this query and #Olaf Dietsche solution (one of the versions) are below.
Data setup script
CREATE TABLE dbo.Test
(
id int not null PRIMARY KEY,
account_num int not null,
status_code nchar(1)
)
GO
INSERT dbo.Test (id, account_num, status_code)
SELECT 64 , 1, 'X' UNION ALL
SELECT 82 , 1, 'Y' UNION ALL
SELECT 72 , 2, 'Y' UNION ALL
SELECT 87 , 1, 'Z' UNION ALL
SELECT 91 , 2, 'X' UNION ALL
SELECT 103, 2, 'Z'
SQL Fiddle with subselect
select id, account_num, status_code
from mytable
where id in (select min(t1.id)
from mytable t1
join mytable t2 on t1.account_num = t2.account_num
and t1.id > t2.id
and t2.status_code = 'X'
group by t1.account_num)
and SQL Fiddle with join, both for MS SQL Server 2012, both returning the same result.
select id, account_num, status_code
from mytable
join (select min(t1.id) as min_id
from mytable t1
join mytable t2 on t1.account_num = t2.account_num
and t1.id > t2.id
and t2.status_code = 'X'
group by t1.account_num) t on id = min_id
SELECT MIN(ID), ACCOUNT_NUM, STATUS_CODE FROM (
SELECT ID, ACCOUNT_NUM, STATUS_CODE
FROM ACCOUNT A1
WHERE EXISTS
(SELECT 1
FROM ACCOUNT A2
WHERE A1.ACCOUNT_NUM = A2.ACCOUNT_NUM
AND A2.STATUS_CODE = 'X'
AND A2.ID < A1.ID)
) SUB
GROUP BY ACCOUNT_NUM
Here's an SQLFIDDLE
Here's query, with your data, checked under PostgreSQL:
SELECT t0.*
FROM so13594339 t0 JOIN
(SELECT min(t1.id), t1.account_num
FROM so13594339 t1, so13594339 t2
WHERE t1.account_num = t2.account_num AND t1.id > t2.id AND t2.status_code = 'X'
GROUP BY t1.account_num
) z
ON t0.id = z.min AND t0.account_num = z.account_num;

Join and a Pivot statement SQL Server

I want to pivot and join to select from 3 tables
Table 1: INT,VARCHAR,FLOAT
ID Name value
---------------------------
1 a1 32116580
2 a2 50785384
3 a3 54327508
4 a4 61030844
Table 2: INT, VARCHAR, FLOAT
ID Name value
---------------------------
1 x11 61326085092
2 x12 80368184260
3 x13 83023398776
4 x14 91144307692
5 x22 95486535484
6 x23 90357090612
7 x24 100588807668
8 x33 707811916752
9 x34 93128452928
10 x44 84566653668
Table 3: INT, VARCHAR, FLOAT
ID Name value
---------------------------
1 q1 61326085092
2 q2 95486535484
3 q3 707811916752
4 q4 84566653668
output table:
column1 column2 column3 column4
--------------------------------------------------------------------------
a1*a1/(q1+q1+x11) a1*a2/(q1+q2+x12) a1*a3/(q1+q3+x13) a1*a4/(q1+q4+x14)
null a2*a2/(q2+q2+x22) a2*a3/(q2+q3+x23) a2*a4/(q2+q4+x24)
null null a3*a3/(q3+q3+x339 a3*a4/(q3+q4+x34)
null null null a4*a4/(q4+q4+x44)
(I'm putting the 'Name' of the column of the 3 different tables instead of numbers)
How to do this?
I guess I have to do two pivots? and
unpivot?...
Well do not how to complete it..
SELECT *
FROM (
SELECT
t1.a1,
t1.a2,
t2.x,
t3.q
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.id
...
) Output
PIVOT (
name IN (
...
PIVOT(name ... )
)
) PivotTable
UPDATE
Previously I have *'s I have changed it to division and sum, the *'s were just an example,
Sample tables
create table Table1(ID int, Name varchar(10), value float)
insert table1 select
1 ,'a1', 32116580 union all select
2 ,'a2', 50785384 union all select
3 ,'a3', 54327508 union all select
4 ,'a4', 61030844
create table Table2(ID int, Name varchar(10), value float)
insert Table2 select
1 ,'x11', 61326085092 union all select
2 ,'x12', 80368184260 union all select
3 ,'x13', 83023398776 union all select
4 ,'x14', 91144307692 union all select
5 ,'x22', 95486535484 union all select
6 ,'x23', 90357090612 union all select
7 ,'x24', 100588807668 union all select
8 ,'x33', 707811916752 union all select
9 ,'x34', 93128452928 union all select
10 ,'x44', 84566653668
create table Table3(ID int, Name varchar(10), value float)
insert Table3 select
1 ,'q1', 61326085092 union all select
2 ,'q2', 95486535484 union all select
3 ,'q3', 707811916752 union all select
4 ,'q4', 84566653668
The query you need, for N = 4. For any other N, just use dynamic SQL to build the query, changing the 2 lines required as indicated by **.
;with coords(i,row,col,total,N) as (
select 1,1,1,N.N*(N.N+1)/2, N.N
from (select count(*) N from table1) N
union all
select i+1,
case when col+1>N then row+1 else row end,
case when col+1>N then row+1 else col+1 end,
total, N
from coords
where i<total
)
select [1],[2],[3],[4] -- **, e.g. ,[5],[6],etc
from
(
select
c.row,
c.col,
cellvalue= ar.value*ac.value/(qr.value+qc.value+x.value)
from coords c
inner join table1 ar on ar.id = c.row
inner join table1 ac on ac.id = c.col
inner join table3 qr on qr.id = c.row
inner join table3 qc on qc.ID = c.col
inner join table2 x on x.ID = c.i
) p
pivot (max(cellvalue) for col in ([1],[2],[3],[4])) pv -- **
order by row
Output:
1 2 3 4
---------------------- ---------------------- ---------------------- ----------------------
5606.50338459295 6876.83326310711 2047.51559459649 8269.17991568225
NULL 9003.55641750708 3087.36780924588 11044.2303130135
NULL NULL 1389.95405212248 3744.35614651666
NULL NULL NULL 14681.7678040306
The dynamic version
declare #Sql nvarchar(max)
select #Sql = ISNULL(#sql + ',', '') + QUOTENAME(RIGHT(number,10))
from master..spt_values
where type='P' and number between 1 and (select COUNT(*) From table1)
set #Sql = '
;with coords(i,row,col,total,N) as (
select 1,1,1,N.N*(N.N+1)/2, N.N
from (select count(*) N from table1) N
union all
select i+1,
case when col+1>N then row+1 else row end,
case when col+1>N then row+1 else col+1 end,
total, N
from coords
where i<total
)
select ' + #sql + '
from
(
select
c.row,
c.col,
cellvalue= ar.value*ac.value/(qr.value+qc.value+x.value)
from coords c
inner join table1 ar on ar.id = c.row
inner join table1 ac on ac.id = c.col
inner join table3 qr on qr.id = c.row
inner join table3 qc on qc.ID = c.col
inner join table2 x on x.ID = c.i
) p
pivot (max(cellvalue) for col in (' + #sql + ')) pv
order by row
option (maxrecursion 0) -- ! use with caution
'
exec(#sql)