How to transpose SQL output on conditional basis? - sql

I am trying to write a query to transpose rows to columns. The attached image has a sample table which I want to transpose and expected output.
Any input is appreciated.

Option 1. Conditional aggregation
select labid, subjectid,
max(case when timepoint='T0' then val1 end) T0_val1,
max(case when timepoint='T0' then val2 end) T0_val2,
max(case when timepoint='T1' then val1 end) T1_val1,
max(case when timepoint='T1' then val2 end) T1_val2
from input
group by labid, subjectid;
Option 2. Unpivot/Pivot
select * from
(
select labid, subjectid, timepoint+'_'+col as timepoint_col, val
from
(select labid, subjectid, timepoint, val1, val2
from input) as src
unpivot
(
val for col in (val1, val2)
) as unpiv) as x
pivot
(
max(val)
for timepoint_col in ([T0_val1],[T0_val2],[T1_val1],[T1_val2])
) as piv1

Related

How to create a pivot table in PostgreSQL

I am looking to essentially create a pivot view using PostgreSQL, such that the table below:
Column A
Column B
Happy
Sad
Sad
Happy
Happy
Sad
becomes
Count
Column A
Column B
Happy
2
1
Sad
1
2
I've been able to use case/when operators far enough such that I can see the counts under independent columns,
SELECT
COUNT(CASE WHEN column1 = 'Happy' THEN 1 END) AS column1_Happy_count,
COUNT(CASE WHEN column1 = 'Sad' THEN 1 END) AS column1_Sad_count,
COUNT(CASE WHEN column2 = 'Happy' THEN 1 END) AS column2_Happy_count,
COUNT(CASE WHEN column2 = 'Sad' THEN 1 END) AS column2_Sad_count,
COUNT(CASE WHEN column3 = 'Happy' THEN 1 END) AS column3_Happy_count,
COUNT(CASE WHEN column3 = 'Sad' THEN 1 END) AS column3_Sad_count
FROM your_table;
but am missing the step to essentially each the pair of columns vertically.
I'm unable to use extensions such as tablefunc and crosstab.
Try this:
CREATE TABLE my_table (
column_a varchar(10),
column_b varchar(10)
);
INSERT INTO my_table (column_a, column_b)
VALUES ('Happy', 'Sad'),
('Sad', 'Happy'),
('Happy', 'Sad'),
('Good', 'Bad');
WITH DataSource (col, val) AS
(
SELECT 'a', column_a
FROM my_table
UNION ALL
SELECT 'b', column_b
FROM my_table
)
SELECT uniq.val AS "Count"
,MAX(case when counts.col = 'a' then counts end) AS "Column A"
,MAX(case when counts.col = 'b' then counts end) AS "Column B"
FROM
(
SELECT DISTINCT val
FROM DataSource
) uniq
INNER JOIN
(
SELECT col
,val
,COUNT(*) counts
FROM DataSource
GROUP BY col
,val
) counts
ON uniq.val = counts.val
GROUP BY uniq.val
will give you this:
You may aggregate for ColumnA, aggregate for ColumnB then do a full join as the following:
select coalesce(A.ColumnA ,B.ColumnB) as "Count",
A.cnt as "Column A",
B.cnt as "Column B"
from
(
select ColumnA, count(*) cnt
from tbl_name
group by ColumnA
) A
full join
(
select ColumnB, count(*) cnt
from tbl_name
group by ColumnB
) B
on A.ColumnA = B.ColumnB
If the distinct values in ColumnA are the same as the distinct values of ColumnB then you can use join instead of the full join.
See demo.

sql transformation - rows to column

i have been trying to solve this one image
my initial idea is like this
select name,
CASE
when count(name) = 1 then get first distinct value
when count(name) = 2 then get first distinct value
else get first distinct value
END as val1,
CASE
when count(name) = 1 then null
when count(name) = 2 then get second distinct value
else get second distinct value
END as val2,
CASE
when count(name) = 1 then null
when count(name) = 2 then null
else get third distinct value
END as val3
into desired_table
from source_table
group by name
is my attempt feasible? if so, how do i access the first, second and third distinct values?
use pivot . Your output table was incorrect. The correct form is available in db<>fiddle.
select name,x as value1,y as value2,z as value3
from
(
select *
from t1
) as SourceTable
pivot
(
max(value) for value in(x,y,z)
) as PivotTable
demo in db<>fiddle
You can use conditional aggregation along with row_number():
select name,
max(case when seqnum = 1 then value end) as value_1,
max(case when seqnum = 2 then value end) as value_2,
max(case when seqnum = 3 then value end) as value_3
into desired_table
from (select s.*,
row_number() over (partition by name order by value) as seqnum
from source_table s
) s
group by name;

Concatenated string split

I have a table Customer in SQL Server 2016 that has a text column with values
I=3;A=500;D=20210422
I would like to split the I=3,A=500 & D=20210422 into 3 columns in a new table or view.
One method is:
select t.*, s.*
from t cross apply
(select max(case when s.value like 'I=%' then stuff(s.value, 1, 2, '') end) as i,
max(case when s.value like 'A=%' then stuff(s.value, 1, 2, '') end) as a,
max(case when s.value like 'D=%' then stuff(s.value, 1, 2, '') end) as d
from string_split(t.col, ';') s
) s;
Here is a db<>fiddle.
Presumably these are just sample values and will vary? If you don't need to guarantee any particular ordering of the columns a simple solution is to use string_split and a conditional case expression to aggregate into new columns:
with v as (
select value, Row_Number() over (order by (select null)) rn
from String_Split('I=3;A=500;D=20210422',';')
)
select
Max(case when rn=1 then value end) Col1,
Max(case when rn=2 then value end) Col2,
Max(case when rn=3 then value end) Col3
from v
This works:
DECLARE #userData TABLE(
var1 varchar(30) NOT NULL,
var2 varchar(30) NOT NULL,
var3 varchar(30) NOT NULL
);
WITH CTE_Data
AS(
SELECT 'I=3,A=500,D=20210422' AS [MyData]
)
,CTE_Detaildata
AS(
SELECT
P.*
FROM(
SELECT
LEFT(v.[Value], 1) [ID],
v.[Value] AS [Data]
FROM CTE_Data AS d
CROSS APPLY STRING_SPLIT(d.[MyData], ',') AS v
)AS D
PIVOT(MIN(D.[Data]) FOR D.[ID] IN ([I],[A],[D])) AS P
)
INSERT INTO #userData(var1, var2, var3)
SELECT
d.[A],
d.[D],
d.[I]
FROM CTE_Detaildata AS d
SELECT * FROM #userData;

SQL Substring to fetch values which inside Bracket only i.e. ()

Might be easy for you expert but I am finding a challenge here-
My column data is as like below, I need to separate out the value which is inside bracket(). My string pattern would always be like this.
J Zeneta (A50103050); S Rao (B499487)
Output should be
Col1 Col2
A50103050 B499487
You can try this, it might need tweaking depending on your RDBMS, this is for SQLServer
select Left(value, CharIndex(')',value)-1) from (
select value from String_Split('J Zeneta (A50103050); S Rao (B499487)','(')
where value like '%)%'
)x
Edit
To have columns a simple case can yeild, like so
select max(case when rn=1 then Value end) Col1, max(case when rn=2 then value end) Col2 from (
select Left(value, CharIndex(')',value)-1) [Value], Row_Number() over (order by (select null)) rn from (
select value from String_Split('J Zeneta (A50103050); S Rao (B499487)','(')
where value like '%)%'
)x
)x
Edit 2
Using data from an existing table example
create table #Test (id int, col varchar(50))
insert into #Test select 1, 'J Zeneta (A50103050); S Rao (B499487)'
insert into #Test select 2, 'Bob Builder (12345); Mr Rambo (67890)'
select id, max(case when (rn % 2)=0 then Value end) Col1, max(case when (rn % 2)!=0 then value end) Col2 from (
select id, Left(value, CharIndex(')',value)-1) [Value], Row_Number() over (order by (select null)) rn from (
select id, value
from #Test t cross apply String_Split(t.col,'(')
where value like '%)%'
)x
)x
group by id

convert row into column with asc order in oracle

SELECT * FROM
(
SELECT TEST_NAME, SBNO, VAL
FROM TABLE1
)
PIVOT (
MAX(VAL)
FOR SBNO in (1 value1, 2 value2, 3 value3 ));
Output:
I want to show data in ascending order, like this:
2.3 , 2.4 , 2.5
but result is
2.3 , 2.5 , 2.4
This would do the trick. I have created a new SBNO column, ordering by test_name and value and then PIVOT operator will do the rest.
;with cte as (
select
test_name
,val
,'Value' + cast(ROW_NUMBER() over (partition by test_name order by test_name, val) as varchar(5)) as new_SBNO
from TABLE1
)
select *
from cte
pivot (
max(val)
for new_SBNO in (value1, value2, value3)
) pvt
I would do this using conditional aggregation:
select test_name,
max(case when seqnum = 1 then val end) as value_1,
max(case when seqnum = 2 then val end) as value_2,
max(case when seqnum = 3 then val end) as value_3
from (select t1.*,
row_number() over (partition by test_name order by val asc) as seqnum
from table1 t1
) t1
group by test_name;
You could express this with pivot. And in Oracle, pivot can even be a bit faster. I just find conditional aggregation to be more flexible and simpler to implement.