How to convert a single row table into columns? - sql

I have a single row query returning data in this format:
Col1 Col2 Col3 Col4
-----------------------------
1425 3454 2345 3243
I want it to display it in this format:
Col1 | 1425
Col2 | 3454
Col3 | 2345
Col4 | 3243
How can I do it?
I am avoiding to use UNION method since the above table is extracted from a query and for each <table_name> I would have to paste the table query which will make the process slow.

If the number of fields per table is always constant, then it might work like this.
DECLARE #Table TABLE(
[Col1] int,
[Col2] int,
[Col3] int,
[Col4] int
)
INSERT INTO #Table VALUES(1425, 3454, 2345, 3243); -- some Test data
SELECT * FROM #Table; -- row
SELECT
p.[Columns],
p.[Value]
FROM (
SELECT
[Col1],
[Col2],
[Col3],
[Col4]
FROM #Table
) x
UNPIVOT(
[Value] FOR [Columns] IN ([Col1],[Col2],[Col3],[Col4]) --
) AS P;

You can cross join your query with the column names in order to show the column values in separate rows:
select
columns.col,
case columns.col
when 'Col1' then q.col1
when 'Col2' then q.col2
when 'Col3' then q.col3
when 'Col4' then q.col4
end as value
from ( <your query here> ) q
cross join ( values ('Col1'), ('Col2'), ('Col3'), ('Col4') ) as columns(col);

Related

SQL command to break up a column into additional columns

I am trying to take one column in my table that has multiple values (up to 10) delimited by a "pipe" "|" in it and add the delimited values into additional columns in the table. Note (running on SQL SVR 2014).
Table ...
Col1 Col2 Col3
1 Tom 12345678|87654321|11111111|22222222|..... up to 10
2 Joe 14563467
3 Zac 12345678|87654321
I need the results of SQL to produce
Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 ....
1 Tom 12345678 87654321 11111111 22222222
2 Joe 14563467
3 Zac 12345678 87654321
Any help is appreciated!
You can first normalize your data using the new STRING_SPLIT function into a derived table with the split columns stretched downward. Using that table you can PIVOT out based on Col1 to create the 5 columns basically pulling the data back up that was previously split down. Next, use that data as the source for you update back to the source table.
If you are not on SQL Server 2016 then you will need to replace STRING_SPLIT with an delimited string parser Table Value Function .
DECLARE #T TABLE(Col1 INT, Col2 NVARCHAR(50), Col3 NVARCHAR(50), Col4 NVARCHAR(50), Col5 NVARCHAR(50))
INSERT #T (COl1,Col2) VALUES (1,'12345678|87654321|11111111|22222222|')
INSERT #T (COl1,Col2) VALUES (2,'12345678')
INSERT #T (COl1,Col2) VALUES (3,'12345678|87654321|')
SELECT * FROM #T
;
WITH SplitData AS
(
SELECT
Col1,Col2 = S.Value,
RN = ROW_NUMBER()OVER( PARTITION BY Col1 ORDER BY (SELECT 1 AS X))
FROM
#T
CROSS APPLY STRING_SPLIT(Col2,'|') S
)
,UpdateData AS
(
SELECT
Col1, Col2=[1], Col3=[2], Col4=[3], Col5=[4]
FROM
(
SELECT Col1, Col2, RN FROM SplitData
) AS S
PIVOT(
MAX(Col2) FOR RN IN ([1], [2], [3], [4], [5])
) AS P
)
UPDATE L
SET L.Col1 = R.Col1, L.Col2=R.Col2, L.Col3=R.Col3, L.Col4=R.Col4, L.Col5 = R.Col5
FROM
#T L
INNER JOIN UpdateData R ON L.Col1 = R.Col1
SELECT * FROM #T
If u are sure max values count in Col3 is 10 u can use cross apply with split string on Col3 and select columns with splited value and row number partitioned by col1 and col2. Secound step is pivot this table by row
WITH SplitTable AS(
SELECT Col1
,Col2
,value
,ROW_NUMBER() OVER(PARTITION BY Col1, Col2 ORDER BY (SELECT NULL)) as rn
FROM YOUR_TABLE
CROSS APPLY STRING_SPLIT(Col3, '|') AS YT
)
SELECT Col1
,Col2
,[1]
,[2]
,[3]
,[4]
,[5]
,[6]
,[7]
,[8]
,[9]
,[10]
FROM SplitTable
PIVOT(
MAX(VALUE)
FOR RN IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])
) as PVT

How to write a SQL query for the below?

I have two tables with n of columns from Col1 to Col30
Table 1.
Templateid Col1 Col2 Col3 Col4 ...
95 2019-05-28 1234 test123 123456
Table 2.
Templateid DisplayName ColumnName
95 date col1
95 rank col2
95 purpose col3
95 sign col4
Expected Results.
Col1Name Col1Value Col2Name Col2Value Col3Name Col3Value ....
date 2019-05-28 rank 1234 purpose test123
This is a crude way of doing it and if you do not know the number of columns in each table you would need to use dynamic sql to enumerate them out but for the purposes of this example I have assumed you do know the number of columns and the names you want to populate.
The union query allows you to pre-populate the desired column names using the col1 syntax, then the pivot allows you to match up the displaynames and the display values. A case statement is required to ensure the correct values are shown and you do need to populate your derived column names for the pivot query but you do get the desired outcome this way.
declare #table1 table (
Templateid int,
Col1 date,
col2 int,
col3 nvarchar(10),
col4 int
);
insert into #table1 (Templateid, col1, col2, col3, col4)
values
(95, '2019-05-28', '1234', 'test123', '123456');
declare #table2 table (
Templateid int,
Displayname nvarchar(10),
ColumnName nvarchar(10)
);
insert into #table2 (Templateid, Displayname, ColumnName)
values
(95, 'date', 'col1'),
(95, 'rank', 'col2'),
(95, 'purpose', 'col3'),
(95, 'sign', 'col4');
select * from
(
select columnname+'Name' as columnname, Displayname
from #table2 t2
union
select columnname+'Value', case when columnname='col1' then cast(col1 as nvarchar(15))
when columnname='col2' then cast(col2 as nvarchar(15))
when columnname='col3' then cast(col3 as nvarchar(15))
when columnname='col4' then cast(col4 as nvarchar(15)) end
from #table1 t1 inner join #table2 t2 on t1.Templateid=t2.Templateid) src
pivot
(max(displayname) for columnname in ([col1Name],[col1Value], [col2Name],[col2Value], [col3Name],[col3Value], [col4Name],[col4Value])) piv;

How to convert multiple columns into a single row with row number?

I'm trying to figure out a way to convert
[Column1],[Column2],[Column3],[Column4],[Column5] into a single column [Type]. I also want it to have row number. So an example of how I would want it to look like. ALso, what if I just want column with data to output? Like Column1 and Column2 both have values but column3, 4, and 5 don't. In this case, I just want 1 and 2 to show on my resultset.
Now:
[ID1]-[Column1],[Column2],[Column3],[Column4],[Column5]
[ID2]-[Column1],[Column2],[Column3],[Column4],[Column5]
Desire:
[ID1]-[Column1],[Row1]
[ID1]-[Column2],[Row2]
[ID1]-[Column3],[Row3]
[ID1]-[Column4],[Row4]
[ID1]-[Column5],[Row5]
[ID2]-[Column1],[Row1]
[ID2]-[Column2],[Row2]
Etc......
Thank you!
Try some thing like this but this is may not reliable solution but get work done first
SELECT Col,Row_NUMBER() OVER(ORDER BY [ID]) RowNo
FROM (
SELECT [ID],CAST([ID] AS VARCHAR(30))+'-'+[COLUMN1] as Col
FROM TableName
UNION ALL
SELECT [ID],CAST([ID] AS VARCHAR(30))+'-'+[COLUMN2] as Col
FROM TableName
UNION ALL
SELECT [ID],CAST([ID] AS VARCHAR(30))+'-'+[COLUMN3] as Col
FROM TableName
...Like wise for other columns
)M
ORDER BY [ID]
one way to unpivot a table is by using Cross/Outer apply and Table Value Constructors.
--sample data
WITH cte AS (
SELECT *
FROM (VALUES (1, 'col1', 'col2', 'col3', 'col4', 'col5'),
(2, 'col1', 'col2', 'col3', 'col4', 'col5'))
t(id, column1,column2,column3,column4,column5)
)
--query
SELECT cte.id,
t.*
FROM cte
OUTER APPLY (VALUES(column1, 1),(column2, 2),(column3, 3),(column4, 4),(column5, 5)) t([type],[rownum])
You can use UNPIVOT to reverse columns to rows:
;WITH YourTable AS (
SELECT *
FROM (VALUES
('ID1','Column11','Column12','Column13','Column14','Column15'),
('ID2','Column21','Column22','Column23','Column24','Column25')
) as t([ID],[Column1],[Column2],[Column3],[Column4],[Column5])
)
SELECT *
FROM (
SELECT [Column1] as [Row1],
[Column2] as [Row2],
[Column3] as [Row3],
[Column4] as [Row4],
[Column5] as [Row5],
[ID]
FROM YourTable
) t
UNPIVOT (
[Type] FOR [Column] IN ([Row1],[Row2],[Row3],[Row4],[Row5])
) as unp
Output:
ID Type Column
ID1 Column11 Row1
ID1 Column12 Row2
ID1 Column13 Row3
ID1 Column14 Row4
ID1 Column15 Row5
ID2 Column21 Row1
ID2 Column22 Row2
ID2 Column23 Row3
ID2 Column24 Row4
ID2 Column25 Row5

How to make dynamic column

I have some data as under
Declare #t table (Id int identity,CommaSeperatedValue varchar(100))
Insert Into #t
Select 'Somalia,Vietnam' Union All
Select 'apple,banana,guava,India,Australia'
There is no limit in the CommaSeperated value. The desired output for the sample provided will be
Id Col1 Col2 Col3 Col4 Col5
1 Somalia Vietnam Null Null Null
2 apple banana guava India Australia
That means , the columns will be generated dynamically. Let us take another example
Declare #t table (Id int identity,CommaSeperatedValue varchar(100))
Insert Into #t
Select 'Somalia,Vietnam,Honolulu,Spain' Union All
Select 'apple,banana,guava,India,Australia,Smart,Bus' Union All
Select 'Mango'
The desired output
Id Col1 Col2 Col3 Col4 Col5 Col6 Col7
1 Somalia Vietnam Honolulu Spain Null Null Null
2 apple banana guava India Australia Smart Bus
3 Mango Null Null Null Null Null Null
How to do this query?
My attempt so far(after this I am lost)
SELECT
X.id,
X.CommaSeperatedValue,
Y.splitdata
FROM
(
SELECT *,
CAST('<X>'+REPLACE(F.CommaSeperatedValue,',','</X><X>')+'</X>' AS XML) AS xmlfilter
FROM #t F
)X
CROSS APPLY
(
SELECT fdata.D.value('.','varchar(50)') as splitdata
FROM X.xmlfilter.nodes('X') as fdata(D)
)Y
Thanks in advance
Well here is the Dynamic Solution you are looking for .I used Temp Table you can replace it with Permanent Table or Table Variable.
Declare #t table (Id int identity,CommaSeperatedValue varchar(100))
Insert Into #t
Select 'Somalia,Vietnam' Union All
Select 'apple,banana,guava,India,Australia'
IF object_ID('TempDB..#Temp') IS NOT NULL DROP TABLE #Temp;
SELECT
X.id,
--X.CommaSeperatedValue,
Y.splitdata
,ROW_NUMBER() OVER( PARTITION BY X.id ORDER BY X.id ) AS DataID
INTO #Temp
FROM
(
SELECT *,
CAST('<X>'+REPLACE(F.CommaSeperatedValue,',','</X><X>')+'</X>' AS XML) AS xmlfilter
FROM #t F
)X
CROSS APPLY
(
SELECT fdata.D.value('.','varchar(50)') as splitdata
FROM X.xmlfilter.nodes('X') as fdata(D)
)Y
DECLARE #MAXCol INT = (SELECT MAX(DataID)FROM #Temp)
,#index INT =1
,#ColNames varchar(4000)=''
WHILE (#index<=#MAXCol)
BEGIN
SET #ColNames =#ColNames +'MAX(CASE WHEN DataID = '+LTRIM(STR(#index))+' THEN splitdata END) as Col'+LTRIM(STR(#index))+','
SET #Index=#Index +1
END
SET #ColNames = LEFT(#ColNames,LEN(#ColNames)-1) -- Remove Last Comma
EXECUTE ( 'SELECT
[id],'+#ColNames+' FROM #Temp GROUP BY [id]'
)
Going with what you already got, add a rownumber, and transpose it using a group by.
SELECT
[id],
MAX(CASE WHEN RowNumber=1 THEN splitdata END) as Col1,
MAX(CASE WHEN RowNumber=2 THEN splitdata END) as Col2,
MAX(CASE WHEN RowNumber=3 THEN splitdata END) as Col3,
MAX(CASE WHEN RowNumber=4 THEN splitdata END) as Col4,
MAX(CASE WHEN RowNumber=5 THEN splitdata END) as Col5,
MAX(CASE WHEN RowNumber=6 THEN splitdata END) as Col6,
MAX(CASE WHEN RowNumber=7 THEN splitdata END) as Col7,
MAX(CASE WHEN RowNumber=8 THEN splitdata END) as Col8,
MAX(CASE WHEN RowNumber=9 THEN splitdata END) as Col9,
MAX(CASE WHEN RowNumber=10 THEN splitdata END) as Col10
FROM (
SELECT
X.id,
Y.splitdata,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) AS RowNumber
FROM
(
SELECT *,
CAST('<X>'+REPLACE(F.CommaSeperatedValue,',','</X><X>')+'</X>' AS XML) AS xmlfilter
FROM #t F
)X
CROSS APPLY
(
SELECT fdata.D.value('.','varchar(50)') as splitdata
FROM X.xmlfilter.nodes('X') as fdata(D)
)Y
) X
GROUP BY [id]
This will yield:
id Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10
1 Somalia Vietnam Honolulu Spain NULL NULL NULL NULL NULL NULL
2 apple banana guava India Australia Smart Bus NULL NULL NULL
3 Mango NULL NULL NULL NULL NULL NULL NULL NULL NULL
I believe you would need to use dynamic SQL to do this, because the SELECT statement needs to specify the number of columns, or you need to iterate over the number of items in the CSV column.
Typically this is a bad idea, will cause more problems than it solves, and is too confusing for the next person to maintain. What you might want to consider instead is simply flattening your data to a two columned format
-- ie your initial data
1, 'Somalia,Vietnam,Honolulu,Spain'
2, 'apple,banana,guava,India,Australia,Smart,Bus'
3, 'Mango'
-- would become
1, 'Somalia'
1, 'Vietnam'
1, 'Honolulu'
1, 'Spain'
2, 'apple'
2, 'banana
-- etc
Now group and pivot in your presentation layer.
Trying to format data in SQL (probably for a report or data export?) is a common mistake, really just one up from trying to store dates as literals. SQL is a data storage and manipulation language / platform; it is not for data Trying to use it in this manner will simply cause yourself no end of headache.

How to convert a column header and its value into row in sql?

I have a table with columns say col1, col2, col3. The table has many rows in it.
Let's assume val1, val2, val3 is one such row. I want to get the result as
Col1, Val1
Col2, Val2
Col3, Val3
That is 3 rows - one for each column and its value.
I am using SQL Server 2008. I read about pivots. Are pivots a way to solve this problem? Can someone route me to some examples or solutions how to solve this problem?
Thanks a lot
Maybe something like this:
Test data
DECLARE #T TABLE(Col1 INT, Col2 INT, Col3 INT)
INSERT INTO #T
VALUES (1,1,1)
Query
SELECT
*
FROM
(
SELECT
t.Col1,
t.Col2,
t.Col3
FROM
#T AS t
) AS SourceTable
UNPIVOT
(
Value FOR Col IN
(Col1,Col2,Col3)
) AS unpvt
Output
1 Col1
1 Col2
1 Col3
To do this kind of thing read the following: Using PIVOT and UNPIVOT
Pivot function allow you to convert row values in from of column..
Also check : Dynamic Pivoting in SQL Server
Example :
create table #temptable(colorname varchar(25),Hexa varchar(7),rgb varchar(1), rgbvalue tinyint)
GO
insert into #temptable values('Violet','#8B00FF','r',139);
insert into #temptable values('Violet','#8B00FF','g',0);
insert into #temptable values('Violet','#8B00FF','b',255);
insert into #temptable values('Indigo','#4B0082','r',75);
insert into #temptable values('Indigo','#4B0082','g',0);
insert into #temptable values('Indigo','#4B0082','b',130);
insert into #temptable values('Blue','#0000FF','r',0);
insert into #temptable values('Blue','#0000FF','g',0);
insert into #temptable values('Blue','#0000FF','b',255);
SELECT colorname,hexa,[r], [g], [b]
FROM
(SELECT colorname,hexa,rgb,rgbvalue
FROM #temptable) AS TableToBePivoted
PIVOT
(
sum(rgbvalue)
FOR rgb IN ([r], [g], [b])
) AS PivotedTable;
Create a temproary table:
CREATE TABLE #table2
(
name NCHAR,
bonus INT
)
Now Select and execute the below statement if there is an empty.
SELECT * FROM #table2
INSERT INTO #table2 (name,bonus) VALUES ('A',10)
INSERT INTO #table2 (name,bonus) VALUES ('B',20)
INSERT INTO #table2 (name,bonus) VALUES ('C',30)
After insert the values into table. select and execute the below line if you get records:
SELECT * FROM #table2
Input:
name bonus
A 10
B 20
C 30
Change the input into like this result
Result:
Cost A B C
Bonus 10 20 30
By using this code:
SELECT 'Bonus' AS Cost,
[A],[B],[C]
FROM
(SELECT name, Bonus
FROM #table2) AS TempTbl
PIVOT
(
AVG(bonus)
FOR [name] IN ([A],[B],[C])
) AS PivotTable;