Concatenate dynamically pivoted columns for a variable number of columns - sql-server

I have been able to successfully pivot different field values into separate columns but what I want is to concatenate all of those separate (pivoted) columns into one column. I don't know how many pivoted columns that I end up with at runtime so I can't just use Column1 + Column2 + Column3 etc.
Here is my sql
--Create a table variable to hold my source data
declare #datatable table
(
OrderId int,
ProductId int,
ClientName varchar(50)
)
--insert some data
insert into #datatable values (1, 2, 'Joe Bloggs')
insert into #datatable values (1, 2, 'Frank Bloggs')
--Create a temp table
--that introduces a new field (called Header)
--to give me column names for my pivoted data
IF OBJECT_ID('tempdb..#PivotedClients') IS NOT NULL DROP TABLE #PivotedClients
create table #PivotedClients
(
OrderId int,
ProductId int,
ClientName varchar(50),
Header varchar(100)
)
insert into #PivotedClients
select OrderId,
ProductId,
ClientName,
'Client ' + Cast(Rank() Over (Partition by OrderId
order by ClientName) as varchar(3))
from #datatable
--Create variables to hold my column names
--and my (dynamic) sql
declare #pivotcolumns nvarchar(max),
#colsWithNoNulls nvarchar(max),
#sqlquery nvarchar(max)
set #pivotcolumns = STUFF(
(
select distinct ',' + QUOTENAME(Header)
from #PivotedClients
for XML PATH (''), TYPE
).value('.', 'nvarchar(max)')
,1,1,'')
set #colsWithNoNulls = STUFF(
(
SELECT DISTINCT ',ISNULL(' + QUOTENAME(Header) + ', '''') ' + QUOTENAME(Header)
FROM #PivotedClients
for XML PATH (''), TYPE
).value('.', 'NVARCHAR(max)')
,1,1,''
)
if OBJECT_ID('tempdb..##Clients ') is not null drop TABLE ##Clients
set #sqlquery = 'select distinct OrderId,
ProductID,
' + #colsWithNoNulls + '
into ##Clients
from
(
select OrderId,
ClientName,
ProductID,
Header
from #PivotedClients) x
pivot
(
Max(ClientName)
for Header in (' + #pivotcolumns + ')
) p'
exec sp_executesql #sqlquery
----
select *
from ##Clients
----
The record set that I currently end up with is:
OrderId ProductId Client1 Client 2
1 2 Frank Bloggs Joe Blogs
What I want is:
OrderID ProductId Clients
1 2 Frank Bloggs Joe Bloggs
I don't know how many 'pivoted' columns that I will end up with at runtime so I can't just use Client1 + Client2 etc

Just need to change your #colsWithNoNulls to perform the string concatenation
set #colsWithNoNulls = STUFF(
(
SELECT DISTINCT '+ '' '' + ISNULL(' + QUOTENAME(Header) + ', '''')'
FROM #PivotedClients
for XML PATH (''), TYPE
).value('.', 'NVARCHAR(max)')
, 1, 1, '') + 'AS Clients'
Also you can do a print #sqlquery before sp_executesql. this will helps you debug your dynamic query

Related

MS SQL.Transform rows into columns

I have a simple table. As a result I need to get names of numeric columns and paste like rows and then paste its values like columns.Here is an example of table:
As a result I need to receive something like this:
I tried to get the result with PIVOT, but I have not the correct answer.
select * from (
select col1, 'val' + cast(row_number()over(partition by col1 order by col1) as nvarchar(20)) ColVal
from mytbl
) tmp
pivot (
min(col1) for ColVal in (val1,val2)
) pvt
In this case you need to unpivot first, then pivot back:
DROP TABLE IF EXISTS dbo.temp
DROP TABLE IF EXISTS dbo.temp2
CREATE table dbo.temp(col1 INT, col2 INT, col3 INT);
INSERT INTO temp VALUES (27,93,80),(32,84,72),(46,68,75),(38,79,73),(23,77,84);
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
-- first unpivot to key value pairs
select #colsUnpivot
= stuff((select ','+quotename(C.column_name)
from information_schema.columns as C
where C.table_name = 'temp' and
C.column_name like 'col%'
for xml path('')), 1, 1, '')
set #query
= 'SELECT ''val'' + convert(varchar, i) as id,
name,
val
INTO dbo.temp2
FROM
(
SELECT *, row_number() over (order by col1) as i
from temp
) a
UNPIVOT
(
val
FOR name IN ('+ #colsunpivot +')
) u'
exec sp_executesql #query;
-- now pivot back
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', ' + QUOTENAME(id)
FROM (SELECT DISTINCT id FROM dbo.temp2) AS x;
SET #query = N'
SELECT name, ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT id, name, val
from temp2
) AS j
PIVOT
(
SUM(val) FOR id IN ('
+ STUFF(REPLACE(#columns, ', [', ',['), 1, 1, '')
+ ')
) AS p;';
EXEC sp_executesql #query;
I adapted two separate scripts I had lying around, hence the two parts and the intermediate temp2 table. You can probably mash both together with a bit of elbow grease, but this should get you most of the way there.
Also adding the id (to get val1, val2 etc) dynamically means the results are sorted by col1 (val1 will have the lowest col1) but you were doing something similar in your attempt so I assume this is ok. If not, you will need to add an identity column to the data first and use that in place of the row_number()

Split multiple column and store into temp table

I have the sample data:
Table: tblsampledata
create table tblsampledata
(
column1 varchar(50),
column2 varchar(50)
);
insert into tblsampledata values('Bob Frapples','Gail Forcewind');
insert into tblsampledata values('Paul Molive','Mario Speedwagon');
And I have column mapping table with table name:
Table: tblmapping
create table tblmapping
(
tblname varchar(100),
columnmap varchar(max)
);
insert into tblmapping values('tblsampledata','[column1]|[column2]');
Note: I want to split the column data which are exists in tblmapping of table name in column tblname and store it into temp table.
Expected Result: #TempTable
column1 column2
---------------------
Bob Gail
Frapples Forcewind
Paul Mario
Molive Speedwagon
You need to use dynamic query to acheive this.
You can try like following.
select #xml = Cast(( '<X>' + Replace(columnmap, '|', '</X><X>') + '</X>' ) AS XML)
from tblmapping where tblname =#tablename
DECLARE #query AS NVARCHAR(max) = 'select ' + Stuff((SELECT DISTINCT ', ' + value
FROM (
SELECT n.value('.', 'varchar(100)') AS value
FROM #xml.nodes('X') AS T(n)
)t
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
+ ' from ' + #tablename;
exec sp_executesql #query
Online Demo
To split the column 1 and Column 2 you can use query like following.
SELECT CASE
WHEN n = 1 THEN LEFT(column1, Charindex(' ', column1) - 1)
WHEN n = 2 THEN RIGHT(column1, Charindex(' ', Reverse(column1)) - 1)
END AS column1,
CASE
WHEN n = 1 THEN LEFT(column2, Charindex(' ', column2) - 1)
WHEN n = 2 THEN RIGHT(column2, Charindex(' ', Reverse(column2)) - 1)
END AS column2
FROM tblsampledata t1
CROSS JOIN ( VALUES(1),(2) )t(n)
Full Demo using dynamic query

Rows to Columns RDLC

i have data in below format. this data is coming through SQL Query.
i want to show it in below format either by query or by rdlc report.
You need to use dynamic SQL to make it.
From your expected result you can try to follow thoes step to make it.
use row_number function make row number by Name, because we need to join base on that row_number.
get the use MAX and MIN to make row number calendar table. from 1 to max(rn). the table can let use outer join
declare a var #tables to make the OUTER JOIN execute SQL (each LEFT JOIN maen a group of Crew#).
declare a var #col to make column, which you want to select (Employee) from each table.
then use execute dynamic execute it.
look like this.
create table T
(
Name varchar(50),
Employee VARCHAR(50)
)
insert into T values ('Crew#1','TR123');
insert into T values ('Crew#1','311');
insert into T values ('Crew#2','DDD');
insert into T values ('Crew#2','12121');
insert into T values ('Crew#1','SDDAS');
insert into T values ('Crew#3','31114312');
insert into T values ('Crew#3','DD14124D');
insert into T values ('Crew#3','1214124121');
insert into T values ('Crew#3','SDD412AS');
DECLARE #tables AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#col AS NVARCHAR(MAX);
SET #tables = STUFF((SELECT distinct ' LEFT JOIN ' + ' (SELECT * FROM CTE WHERE Name = '''+Name+''') '+QUOTENAME(Name)+' on t1.smallRN = '+QUOTENAME(Name)+'.rn'
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = STUFF((SELECT distinct ', ' + QUOTENAME(Name)+'.Employee as '''+ QUOTENAME(Name) +''''
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = substring(#col,1, len(#col))
set #query = '
WITH CTE AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) rn
FROM T
),CTE1 AS(
SELECT MIN(rn) smallRN,MAX(rn) bigRN
FROM CTE
UNION ALL
SELECT smallRN+1,bigRN
FROM CTE1
WHERE smallRN < bigRN
)
SELECT '+#col+'
FROM CTE1 t1 ' + #tables
execute(#query)
sqlfiddle
Creatin tbale
First we will create a temp table where we will stock the data that you have and your table
create table #table1
(
[Crew Name] varchar(500) ,
Employee varchar(500)
)
INsert into #table1
values (....)
select * from #table1
Dynamic selection
then we will create a dynamic query to get the columns that we have, that way we can add as much crews as we want,
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
this way we will get only the first row for every column
so we have to find a way to aggregate and get the other columns as well just to demonstrate i will union the Mmin also this is where i stoped my testes but you can do more then this with some testes
now the union :
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
union
select ' +#ColumnName + '
from #table1
Pivot ( MIN(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
here is the result :
if you follow this way i'm sure that you will find a way to union all the result
You can add this result into a temp table
then add a column which will be a reference into this temp table
then use pivot function
To know more about pivot Visit :
https://msdn.microsoft.com/en-us/azure/data-lake-analytics/u-sql/pivot-and-unpivot-u-sql
you can use also SSIS to a very handy tool and easy to use
Using dynamic PIVOT if you dont have a set Crew columns.
DECLARE #ColumnString VARCHAR(256)
DECLARE #ColumnHeadrer VARCHAR(256)
DECLARE #sql varchar(1000)
CREATE TABLE #ColumnValue
(
Value VARCHAR(500),
ColumnHeader VARCHAR(256)
)
INSERT INTO #ColumnValue (Value, ColumnHeader)
SELECT DISTINCT '[' + CrewName + ']',
'ISNULL(' + CrewName + ','''') AS ' + CrewName
FROM CrewTable
SELECT #ColumnString = COALESCE(#ColumnString + ',', '') + Value,
#ColumnHeadrer = COALESCE(#ColumnHeadrer + ',', '') + ColumnHeader
FROM #ColumnValue
SET #sql =
'
SELECT ' + #ColumnHeadrer + '
FROM
(
SELECT Employee,
CrewName,
ROW_NUMBER() OVER(PARTITION BY CrewName ORDER BY CrewName) AS rnk
FROM CrewTable
) AS P
PIVOT
(
MAX(Employee) FOR [CrewName] IN ('+#ColumnString+')
) AS pv
'
EXEC (#sql)

How to generate report using PIVOT based on date

I have two tables MasterTableTest8 and HistoricDatatest8.
create table MasterTableTest8
(ID int primary key, Name varchar(10))
insert into MasterTableTest8 values (1,'ATS')
insert into MasterTableTest8 values (2,'BTS')
CREATE TABLE HistoricDatatest8
(
ID int FOREIGN KEY REFERENCES MasterTableTest8(ID),
Name varchar(100),
ShortName varchar(10),
Reason varchar(10),
Importance varchar(10),
Noofissues int,
inserteddate datetime
)
insert into HistoricDatatest8 values (1,'ATS','S', 'Other','High',26,getdate()-7)
insert into HistoricDatatest8 values (1,'ATS','S', 'Other','High',8,getdate()+7)
insert into HistoricDatatest8 values (1,'ATS','S', 'Other','High',80,getdate())
insert into HistoricDatatest8 values (2,'BTS','S1', 'Other','LOW',26,getdate()-7)
insert into HistoricDatatest8 values (2,'BTS','S1', 'Other','LOW',8,getdate()+7)
insert into HistoricDatatest8 values (2,'BTS','S1', 'Other','LOW',80,getdate())
--Created and inserted two tables.
select
N.ID,
N.Name,
ShortName,
Reason,
Importance,
Noofissues,
inserteddate
INTO #TABLE
FROM HistoricDatatest8 N
JOIN MasterTableTest S ON N.ID=S.ID
--Inserting the required data in the Hash table.
--drop table #table
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + CONVERT(NVARCHAR, [inserteddate], 106) + ']',
'[' + CONVERT(NVARCHAR, [inserteddate], 106) + ']')
FROM (SELECT DISTINCT [inserteddate] FROM #TABLE) PV
ORDER BY [inserteddate]
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT * FROM
(
SELECT * FROM #TABLE
) x
PIVOT
(
count(Noofissues)
FOR [inserteddate] IN (' + #cols + ')
) p'
EXEC SP_EXECUTESQL #query
Current Result
Expected Result:
What is your expected result? Your current display shows the same current and expected result.
Are you expecting:
Date | Name | ShortName | Reason | Importance | Number of Issues|
05-Jul-16 | ATS | S| Other| High|26
12-Jul-16|BTS |S1| Other | LOW|80
After lots of R & D i got this result, might be useful to some others. if any optimized answer will be appreciated.
select * from HistoricDatatest80 order by ID
select N.ID,N.Name,ShortName,Reason,Importance,Noofissues,inserteddate INTO #TABLE FROM HistoricDatatest80 N JOIN MasterTableTest80 S ON N.ID=S.ID
--drop table #table
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols +',','')+ '['+DATEValue+']'
FROM (SELECT DISTINCT (CONVERT(NVARCHAR, [inserteddate], 106)) AS DATEValue FROM HistoricDatatest80) PV
ORDER BY DATEValue
print #cols
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT * FROM
(
SELECT ShortName,Reason,Importance,Noofissues,(CONVERT(NVARCHAR, [inserteddate], 106)) AS DATEValue FROM #TABLE
) x
PIVOT
(
sum(Noofissues)
FOR [DATEValue] IN (' + #cols + ')
) p'
EXEC SP_EXECUTESQL #query
drop table #table

TSQL transform row values into one column table

Source Table
Col1 |Col2 |Col3 |Col4 | Col5
----------------------------------
hi | this | is | a | test
Destination Table
RowValues|
----------
hi
this
is
a
test
I am using Dynamic SQL.
Any help ?
This is my code , just change table name and the Id in the where clause to what suits you
DECLARE #sql nVARCHAR(max), #TableName nvarchar(100), #where nvarchar(max)
set #TableName = 'stockItems'
set #where= ' where id = 2'
select #sql ='Select '
select #sql = #sql+ + ' '''+ [name] +' = ''+ cast(' + [name] + ' as nvarchar(10)) as '+[name]+', '
from sys.columns where object_name (object_id) = #TableName
set #sql = stuff(#sql, len(#sql), 1, '') + ' From '+#TableName+ #where
print #sql
set #sql = REPLACE(#sql,', From',' From')
set #sql = #sql + ' '
print #sql
exec(#sql)
Now I need to create a new table that has one column that hold holds each value as a row
Thanks to #Mahmoud-Gamal
The solution should be something like below
declare #cols nvarchar(max)
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(column_name)
FROM information_schema.columns
WHERE table_name = 'vehicles'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
declare #statement nvarchar(max)
set #statement ='
SELECT
ColumnName, Value
FROM
Vehicles
UNPIVOT
(
Value
FOR ColumnName
IN
(
'+#cols+'
)
)
AS A'
execute(#statement)
Please change the "vehicle" table name to any table on your database that has columns from different types (datetime, int and nvarchar) and the below error is shown
Any help ?
The type of column "Description" conflicts with the type of other columns specified in the UNPIVOT list.
Use the UNPIVOT table operator:
SELECT col AS RowValues
FROM table1 AS t
UNPIVOT
(
col
FOR value IN([col1],
[col2],
[col3],
[col4],
[col5])
) AS u;
SQL Fiddle Demo
This will give you:
| ROWVALUES |
|-----------|
| hi |
| this |
| is |
| a |
| test |
Update:
In case you don't know the names of the columns, and you want to do this dynamically, you have to do this using dynamic SQL.
But the problem is how to get the columns names?
You can get the columns names from the information_schema.columns, then concatenate them in one sql, then replace the columns' names in the UNPIVOT with this string, and execute that statement dynamically like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(column_name)
FROM information_schema.columns
WHERE table_name = 'Table1'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = ' SELECT col AS RowValues
FROM table1 AS t
UNPIVOT
(
val
FOR col IN ( ' + #cols + ' )
) AS u;';
EXECUTE(#query);
Updated SQL Fiddle Demo
I believe you want this
Select Col1 + Col2 + Col3 + Col4 + Col5 From Table
Or may be following
Select Col1 From Table1
union Select Col2 From Table1
union Select Col3 From Table1
union Select Col4 From Table1
union Select Col5 From Table1 ;

Resources