Convert rows into columns sql server - sql

I want to convert rows into columns.
Column1 Column2 Column3 Column4 Column5 Column6 Column7
1 2016-07-25 7 3 c1 c11 c111
2 2016-07-26 5 2 c2 c22 c222
3 2016-07-27 1 2 c3 c33 c333
4 2016-07-28 3 1 c4 c44 c444
I want output as follows :
Column1 1 2 3 4
Column2 2016-07-25 2016-07-26 2016-07-27 2016-07-28
Column3 7 5 1 3
Column4 3 2 2 1
Column5 c1 c2 c3 c4
Column6 c11 c22 c33 c44
Column7 c111 c222 c333 c44
I tried to do it using pivot unpivot but did not find proper solution.
First Table (input) can have n number of rows.

You need to use UNPIVOT, then PIVOT and if columns number is unknown - dynamic SQL:
DECLARE #columns nvarchar(max),
#columns_with_convert nvarchar(max),
#row_numbers nvarchar(max),
#sql nvarchar(max)
SELECT #columns = STUFF((
SELECT ','+QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('##YourTable')
FOR XML PATH('')),1,1,'')
--Will get you [Column1],[Column2],[Column3],[Column4],[Column5],[Column6],[Column7]
SELECT #columns_with_convert = (
SELECT 'CAST('+QUOTENAME(name)+' as nvarchar(max)) as '+QUOTENAME(name) +','
FROM sys.columns
WHERE [object_id] = OBJECT_ID('##YourTable')
FOR XML PATH(''))
--Will get you CAST([Column1] as nvarchar(max)) as [Column1], ... because all rows must be same datatype.
SELECT #row_numbers = STUFF((
SELECT ','+ QUOTENAME(CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as nvarchar(max)))
FROM ##YourTable
FOR XML PATH('')),1,1,'')
--This will be used when PIVOTing ([1],[2],[3],[4])
SELECT #sql = '
SELECT *
FROM (
SELECT RN,
[Columns],
[Values]
FROM (
SELECT '+#columns_with_convert+'
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as RN
FROM ##YourTable
) as p
UNPIVOT (
[Values] FOR [Columns] IN ('+#columns+')
) as unpvt
) as s
PIVOT (
MAX([Values]) FOR RN IN ('+#row_numbers+')
) as pvt
'
EXEC sp_executesql #sql
Output:
Columns 1 2 3 4
Column1 1 2 3 4
Column2 2016-07-25 2016-07-26 2016-07-27 2016-07-28
Column3 7 5 1 3
Column4 3 2 2 1
Column5 c1 c2 c3 c4
Column6 c11 c22 c33 c44
Column7 c111 c222 c333 c444
If you PRINT #sql you will get a text of query:
SELECT *
FROM (
SELECT RN,
[Columns],
[Values]
FROM (
SELECT CAST([Column1] as nvarchar(max)) as [Column1],CAST([Column2] as nvarchar(max)) as [Column2],CAST([Column3] as nvarchar(max)) as [Column3],CAST([Column4] as nvarchar(max)) as [Column4],CAST([Column5] as nvarchar(max)) as [Column5],CAST([Column6] as nvarchar(max)) as [Column6],CAST([Column7] as nvarchar(max)) as [Column7],
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as RN
FROM ##YourTable
) as p
UNPIVOT (
[Values] FOR [Columns] IN ([Column1],[Column2],[Column3],[Column4],[Column5],[Column6],[Column7])
) as unpvt
) as s
PIVOT (
MAX([Values]) FOR RN IN ([1],[2],[3],[4])
) as pvt

I tried to do it using pivot unpivot but did not find proper solution.
First Table (input) can have n number of rows.
The reason why it didn't work is because PIVOT does not work for dynamic number of rows. Meaning the syntax can transpose only a fixed number of Rows as Columns and you need to specify that in the query. It cannot Dynamically auto generate columns on the fly based on the values in your rows.
Edit:
One Solution I found for dynamic pivot (1st result from google search) : https://www.mssqltips.com/sqlservertip/2783/script-to-create-dynamic-pivot-queries-in-sql-server/
It needs dynamic SQL (there is no other option)

DECLARE #TABLE TABLE
(RowNo INT,Col1 VARCHAR(10),Col2 VARCHAR(10)
,Col3 VARCHAR(10))
INSERT INTO #TABLE VALUES
(1,'A','B','C')
SELECT Kishore from #Table
Unpivot(Kishore For Value IN (Col1,Col2,Col3)) AS H

Related

transpose of table dynamically different data types

I found this great post for transposing a table in sql:
Simple way to transpose columns and rows in Sql?
edit:
input:
Paul | John | Tim | Eric
Red 'hi' | 5 | 1 | 3.3
Green 'there' | 4 | 3 | 5.5
Blue 'everyone'| 2 | 9 | 7.5
expected output:
Red | Green | Blue
Paul 'hi' | 'there' | 'everyone'
John 5 | 4 | 2
Tim 1 | 3 | 9
Eric 3.3 | 5.5 | 7.5
And I wanted to employ the last dynamic solution for a table that has different data types dynamically:
CREATE TABLE yourTable([color] nvarchar(5), [Paul] nvarchar(10), [John] int, [Tim]
int, [Eric] float);
INSERT INTO yourTable
([color], [Paul], [John], [Tim], [Eric])
VALUES
('Red', 'hi', 5, 1, 3.3),
('Green', 'there', 4, 3, 5.5),
('Blue', 'everyone', 2, 9, 7.5);
When I run the code from the previous answer:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name <> 'color'
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT ','
+ quotename(color)
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select name, '+#colsPivot+'
from
(
select color, name, value
from yourtable
unpivot
(
value for name in ('+#colsUnpivot+')
) unpiv
) src
pivot
(
sum(value)
for color in ('+#colsPivot+')
) piv'
exec(#query)
When I run this code I get the error message:
The type of column "John" conflicts with the type of other columns specified in the UNPIVOT list.
Is there a way that I can use this dynamic solution for my table without losing the dynamic nature of it? I'd like to ideally pass a bunch of tables into this method to transpose them in batch.
Thanks
A method to overcome this would be to use the data type SQL_VARIANT so that the resulting columns can handle more than a single data type. However you cannot SUM() SQL_VARIANT columns, so sum(value) has to be changed to max(value) - or min(value) - but for this pivot that change does not alter the result.
DECLARE #colsConvert AS NVARCHAR(MAX)
DECLARE #colsUnpivot AS NVARCHAR(MAX)
DECLARE #colsPivot as NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
select #colsConvert = (select ', cast('+quotename(C.name)+' as sql_variant) as '+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name <> 'color'
for xml path(''))
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name <> 'color'
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT ','
+ quotename(color)
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select name, '+#colsPivot+'
from
(
select color, name, value
from (select color'+#colsConvert+' from yourtable) as converted
unpivot
(
value for name in ('+#colsUnpivot+')
) unpiv
) src
pivot
(
max(value)
for color in ('+#colsPivot+')
) piv'
exec(#query)
See this working at: http://rextester.com/IBSN39688
Result:
+------+-----+-------+----------+
| name | Red | Green | Blue |
+------+-----+-------+----------+
| Eric | 3.3 | 5.5 | 7.5 |
| John | 5 | 4 | 2 |
| Paul | hi | there | everyone |
| Tim | 1 | 3 | 9 |
+------+-----+-------+----------+
The generated SQL:
select name, [Red],[Green],[Blue]
from
(
select color, name, value
from (select color, cast([Eric] as sql_variant) as [Eric], cast([John] as sql_variant) as [John], cast([Paul] as sql_variant) as [Paul], cast([Tim] as sql_variant) as [Tim] from yourtable) as converted
unpivot
(
value for name in ([Eric],[John],[Paul],[Tim])
) unpiv
) src
pivot
(
max(value)
for color in ([Red],[Green],[Blue])
) piv
+EDIT
An added benefit of using SQL_VARIANT in the result columns is that each standard data type encountered will adopt its default format. Particularly relevant for decimal/float and date/time data. You could also amend the defaults before running the dynamic pivot to further influence the output.
Demonstrated here
The following will transpose virtually any table, view, or query while respecting Row and Column sequences.
Full Disclosure: There is one major drawback. This approach does NOT handle NULL values well. A NULL value will cause the following columns to shift to the left.
Example
Declare #YourTable Table ([Color] varchar(50),[Paul] varchar(50),[John] int,[Tim] int,[Eric] decimal(10,1))
Insert Into #YourTable Values
('Red','hi',5,1,3.3)
,('Green','there',4,3,5.5)
,('Blue','everyone',2,9,7.5)
Declare #XML xml = (Select *,RowNr=Row_Number() over (Order By (Select NULL)) From #YourTable for XML RAW)
Select RowNr = Row_Number() over(Partition By r.value('#RowNr','int') Order By (Select null))
,ColNr = r.value('#RowNr','int')
,Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
Into #Temp
From #XML.nodes('/row') as XN(r)
Cross Apply XN.r.nodes('./#*') AS XA(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('RowNr')
Declare #SQL varchar(max) = '
Select [Item],' + Stuff((Select Distinct ',' + QuoteName(ColNr)+' as '+QuoteName(Value) From #Temp Where RowNr=1 Order by 1 For XML Path('')),1,1,'') + '
From #Temp
Pivot (max([Value]) For [ColNr] in (' + Stuff((Select Distinct ',' + QuoteName(ColNr) From #Temp Order by 1 For XML Path('')),1,1,'') + ') ) p
Where RowNr>1
Order By RowNr'
Exec(#SQL);
Returns
Item Red Green Blue
Paul hi there everyone
John 5 4 2
Tim 1 3 9
Eric 3.3 5.5 7.5
dbFiddle

simple yet twisted SQL query

I have a table say like
X Y
1 3
2 4
Now when a view table in SQL it would show
A1 A2
1 3
2 4
But I want output to be like
A1 A2
X Y
1 3
2 4
Is it possible to do so? Please let me know if I am able to point out my problem.
DECLARE #ColNames VARCHAR(100),#Query VARCHAR(100) = ''
CREATE TABLE Tbl1 (X INT, Y INT) CREATE TABLE Tbl2 (A1 VARCHAR(10), A2 VARCHAR(10)) INSERT INTO Tbl1 (X , Y ) SELECT 1,3 UNION ALL SELECT 2,4
SELECT DISTINCT #ColNames = STUFF( ( SELECT ',' + Col.name + ''
FROM SYS.COLUMNS Col WHERE OBJECT_NAME(Col.OBJECT_ID) = 'Tbl1' FOR XML PATH('') ), 1,1,'') SET #Query = 'SELECT ''' + REPLACE(#ColNames,',',''',''') + ''''
INSERT INTO Tbl2 (A1 , A2 )
EXEC (#Query)
INSERT INTO Tbl2 (A1 , A2 )
SELECT 1,3 UNION ALL
SELECT 2,4
SELECT * FROM Tbl2

SQL complex dynamic Pivoting 2

Hi I am trying in SQL Server the pivoting for the following table
REFID | COL1 | COL2 | Sequence
1 abc cde 1
1 lmn rst 2
1 kna asg 3
2 als zkd 2
2 zpk lad 1
I want the output as
REFID | 1COL1 | 2COL1 | 3COL1 |1COL2|2COL2|3COL2
1 abc lmn kna cde rst asg
2 zpk als null lad zkd null
The number of columns in the original table are known but the number of rows are not known. Can any one help
If you want to include the sequence number as part of your column names, then you will still need to unpivot your col1 and col2 columns first, then apply the pivot. The difference is that you will concatenate the sequence number to your column names created during the unpivot process.
For a known number of values the query would be:
select REFID,
[1col1], [2col1], [3col1],
[1col2], [2col2], [3col2]
from
(
select REFID,
col = cast(Sequence as varchar(10))+ col, value
from yourtable
cross apply
(
select 'COL1', col1 union all
select 'COL2', col2
) c (col, value)
) d
pivot
(
max(value)
for col in ([1col1], [2col1], [3col1],
[1col2], [2col2], [3col2])
) piv
order by refid;
Then if you have an unknown number the dynamic SQL version will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(Sequence as varchar(10))+ col)
from yourtable
cross apply
(
select 'Col1', 1 union all
select 'Col2', 2
) c(col, so)
group by Sequence, col, so
order by so, sequence
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT refid, ' + #cols + '
from
(
select REFID,
col = cast(Sequence as varchar(10))+ col, value
from yourtable
cross apply
(
select ''COL1'', col1 union all
select ''COL2'', col2
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p
order by refid'
execute sp_executesql #query;

SQL complex dynamic Pivoting

Hi I am trying in SQL Server the pivoting for the following table
REFID | COL1 | COL2 | Sequence
1 abc cde 1
1 lmn rst 2
1 kna asg 3
2 als zkd 2
2 zpk lad 1
I want the output as
COLNAME REFID | 1 | 2 | 3
COL1 1 abc lmn kna
COL2 1 cde rst asg
COL1 2 zpk als null
COL2 2 lad zkd null
The number of columns in the original table are known but the number of rows are not known. Can any one help
In order to get the final result, you can use the PIVOT function but first I would unpivot your col1 and col2 columns so you won't have multiple columns to pivot.
If you have a limited number of columns, then you can hard-code the query:
select REFID, col, [1], [2], [3]
from
(
select REFID, Sequence, col, value
from yourtable
cross apply
(
select 'COL1', col1 union all
select 'COL2', col2
) c (col, value)
) d
pivot
(
max(value)
for sequence in ([1], [2], [3])
) piv
order by refid;
But it you have an unknown number of sequence values, then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(sequence)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT refid, col,' + #cols + '
from
(
select REFID, Sequence, col, value
from yourtable
cross apply
(
select ''COL1'', col1 union all
select ''COL2'', col2
) c (col, value)
) x
pivot
(
max(value)
for Sequence in (' + #cols + ')
) p
order by refid'
execute sp_executesql #query;

SQL count ids in fields

I have a table contains IDs in field. It looks like:
FieldName
-------------------------
1,8,2,3,4,10,5,9,6,7
-------------------------
1,8
-------------------------
1,8
I need to count these IDs to get result:
ID | Count
---|------
1 | 3
8 | 3
2 | 1
3 | 1
Any ideas?
Thanks!
Try this :
Declare #demo table(FieldName varchar(100))
insert into #demo values('1,8,2,3,4,10,5,9,6,7')
insert into #demo values('1,8')
insert into #demo values('1,8')
select ID, COUNT(id) ID_count from
(SELECT
CAST(Split.a.value('.', 'VARCHAR(100)') AS INT) AS ID
FROM
(
SELECT CAST ('<M>' + REPLACE(FieldName, ',', '</M><M>') + '</M>' AS XML) AS ID
FROM #demo
) AS A CROSS APPLY ID.nodes ('/M') AS Split(a)) q1
group by ID
I like Devart's answer because of the faster execution. Here is a modified earlier answer to suite your need :
Declare #col varchar(200)
SELECT
#col=(
SELECT FieldName + ','
FROM #demo c
FOR XML PATH('')
);
;with demo as(
select cast(substring(#col,1,charindex(',',#col,1)-1) AS INT) cou,charindex(',',#col,1) pos
union all
select cast(substring(#col,pos+1,charindex(',',#col,pos+1)-pos-1)AS INT) cou,charindex(',',#col,pos+1) pos
from demo where pos<LEN(#col))
select cou ID, COUNT(cou) id_count from demo
group by cou
Try this one -
Query:
SET NOCOUNT ON;
DECLARE #temp TABLE (txt VARCHAR(8000))
INSERT INTO #temp (txt)
VALUES ('1,8,2,3,4,10,5,9,6,7'), ('1,8'), ('1,8')
SELECT
t.ID
, [Count] = COUNT(1)
FROM (
SELECT
ID =
SUBSTRING(
t.string
, number + 1
, ABS(CHARINDEX(',', t.string, number + 1) - number - 1)
)
FROM (
SELECT string = (
SELECT ',' + txt
FROM #temp
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)')
) t
CROSS JOIN [master].dbo.spt_values n
WHERE [type] = 'p'
AND number <= LEN(t.string) - 1
AND SUBSTRING(t.string, number, 1) = ','
) t
GROUP BY t.ID
ORDER BY [Count] DESC
Output:
ID Count
----- -----------
1 3
8 3
9 1
10 1
2 1
3 1
4 1
5 1
6 1
7 1
Query cost: