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()
Related
I'm working on pivot functionality.
I'm having 2 tables one is userinfo and another is hobbies.
My table will be as like below image.
I'm saving the hobbies with comma separated data in userinfo.
I want all the comma separated hobbies with their respective column names.
I tried, but getting indivdual records for each hobbies instead of the single row
as like below image.
My code is as follows :
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
--drop table Temptbl1
--drop table Temptbl2
IF EXISTS (SELECT * FROM Temptbl1)
drop table Temptbl1
IF EXISTS (SELECT * FROM Temptbl2)
drop table Temptbl2
SELECT * INTO Temptbl1 FROM UserInfo CROSS APPLY dbo.SplitData(Hobbies,',')
--select * from Temptbl1
Select * into Temptbl2 from Temptbl1 s,Hobbies h where s.DividedItem=h.Hid
--select * from Temptbl2
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(HName) from Hobbies
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
print #cols
set #query = 'SELECT distinct UId,UName,UAge,Hid, '+#cols +' from ( select
* from Temptbl2 )
x
pivot
(max(DividedItem) for HName in (' + #cols + ') ) p '
select * from Temptbl2
exec(#query)
You are selecting all columns from the temptbl2 and you do not need them. The Hid column is selected there and it is making the rows unique and the pivot fails to aggregate the results correctly. Just change it to this:
set #query = 'SELECT distinct UId,UName,UAge, '+#cols +' from ( select UId,UName,UAge, DividedItem, HName
from Temptbl2 )
x
pivot
(max(DividedItem) for HName in (' + #cols + ') ) p '
And you will be OK.
Here is full working example on SQL Server 2016 SP1. I am using string_split to split the data and temporary table to store it. Also, change a little bit the join syntax (your is obsolete and should not be used). You can adapt the code below to work on your environment easily:
DECLARE #hobbies TABLE
(
[HiD] INT
,[HName] VARCHAR(12)
);
INSERT INTO #hobbies ([HiD], [HName])
VALUES (1, 'Reading')
,(2, 'Singing')
,(3, 'Dancing');
DECLARE #UserInfo TABLE
(
[UID] INT
,[UName] VARCHAR(12)
,[UAddress] VARCHAR(12)
,[UAge] TINYINT
,[Hobbies] VARCHAR(12)
)
INSERT INTO #UserInfo ([UID], [UName], [UAddress], [UAge], [Hobbies])
VALUES (1, 'Abc', 'addr1', 25, '2,3')
,(2, 'Def', 'addr2', 27, '1,2,3')
,(3, 'Ghi', 'addr3', 20, '1');
DROP TABLE IF EXISTS #TEST;
SELECT *
INTO #TEST
FROM #UserInfo UI
CROSS APPLY string_split(UI.[Hobbies], ',') HS
INNER JOIN #hobbies H
ON HS.[value] = h.[HiD]
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
Select #cols = STUFF((SELECT distinct ',' + QUOTENAME(HName) from #hobbies
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = 'SELECT distinct UId,UName,UAge, '+#cols +' from ( select uid, uname, uage, hname, value
from #TEST )
x
pivot
(max(value) for HName in (' + #cols + ') ) p '
exec(#query)
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)
I have two tables. I write a query. Date is dynamic. I can select any date. `
select a.MP,a.CP,a.Frequency,a.Time,CONVERT(varchar(12),b.date,101) as
EntryDate,b.actualtime from mpcp a, DailyData b
where a.UserID=1 and a.MpCpId=b.MpCpId and
CONVERT(varchar(12),b.EntryDate,101) between
CONVERT(varchar(12),GETDATE()-5,101) and
CONVERT(varchar(12),GETDATE()+25,101)`
Output
But i want output like
Assuming that i am storing your result in one temp table and imaging data i created one data for your requirement
try this one whether it is useful or not
create table #piv
(
mp varchar(10),
cp varchar(10),
freq varchar(10),
time int,
entryd date,
acuralize int
)
insert into #piv values
('don','asper','da',30,getdate(),0),
('dwm','donl','da',10,getdate(),3),
('qar','qpr','da',15,getdate(),5),
('qar','qpr','da',15,'01-16-17',5),
('qar','qpr','da',15,'01-15-17',5),
('qar','qpr','da',15,'01-16-17',5)
SELECT * FROM #piv
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(entryd) From #piv Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *,' + #SQL + '
From #piv
Pivot (max(time) For [entryd] in (' + #SQL + ') ) p'
Exec(#SQL);
You can create a dynamic pivot. A good example is available on this link.
Good Luck !
DECLARE #cols VARCHAR(max),#sql VARCHAR(max)
SELECT #cols=ISNULL(#cols+',[','[')+ CONVERT(VARCHAR,a.EntryDate,101)+']'
FROM mpcp a, DailyData b
where a.UserID=1 and a.MpCpId=b.MpCpId and DATEDIFF(d,GETDATE(),b.EntryDate) BETWEEN -5 AND 25
GROUP BY a.time
SET #sql='
SELECT * FROM (
SELECT a.MP,a.CP,a.Frequency,a.Time,CONVERT(varchar(12),b.date,101) AS EntryDate,b.actualtime
FROM mpcp a, DailyData b
WHERE a.UserID=1 and a.MpCpId=b.MpCpId and where a.UserID=1 and a.MpCpId=b.MpCpId and DATEDIFF(d,GETDATE(),b.EntryDate) BETWEEN -5 AND 25
) AS t
PIVOT (MAX(actualtime) FOR EntryDate IN ('+#cols+') )'
EXEC(#sql)
I have a really wide table. All I want to do is put each value for the row into a single column. I also cannot hard code the column names as ill perform this operation on a number of different tables. So, it has to be dynamic. Lastly, please assume that there is only ever one row in the table.
Is there a simple way to UNPIVOT every value in a table?
Current
Col_A Col_B Col_C Col_D
a b c d
Desired Result
Col_Index Col_Value
1 a
2 b
3 c
4 d
Traditional examples of unpivot are not helpful as i need a simple, and dynamic, way of unpivoting every row/column. Unpivot is applicable to one or several, not all.
This should get the ordinal position as Col_Index and the converted fields as Col_Value dynamically
DECLARE #sql NVARCHAR(MAX),
#columns NVARCHAR(Max),
#ordinals NVARCHAR(Max),
#table VARCHAR(200) = 'table'
SELECT #columns = STUFF ((SELECT ',CAST(' + QUOTENAME(column_name) + ' AS VARCHAR(MAX)) AS ' + QUOTENAME(ORDINAL_POSITION)
FROM information_schema.columns
WHERE table_name=#table
ORDER BY ORDINAL_POSITION FOR XML PATH('')), 1, 1, ''),
#ordinals = STUFF ((SELECT ',' + QUOTENAME(ORDINAL_POSITION)
FROM information_schema.columns
WHERE table_name=#table
ORDER BY ORDINAL_POSITION FOR XML PATH('')), 1, 1, '')
SET #sql = N'
SELECT Col_Index,
Col_Value
FROM ( SELECT ' + #columns + ' FROM ' + #table + ') t
UNPIVOT (
Col_Value
FOR Col_Index in (' + #ordinals + ')
) up
'
if you have multiple schemas, you will need to define that along with the table name, and prepend that to the table name in your select query
select a1.*,ROW_NUMBER() over (order by (select null)) as col_index from #t t
cross apply
(
values(col_a),
(col_b),
(col_c),
(col_d)
)a1(val)
CREATE Table
ColTable
(
Col_A VARCHAR(256),
Col_B VARCHAR(256),
Col_C VARCHAR(256),
Col_D VARCHAR(256),
)
INSERT INTO ColTable (Col_A,Col_B,Col_C,Col_D)
Values ('a','b','c','d')
SELECT * FROM ColTable
SELECT
Ordinal AS Col_Index
,ColInfo AS Col_Value
FROM
(
SELECT
Col_A
,Col_B
,Col_C
,Col_D
,1 AS [OrdinalA]
,2 AS [OrdinalB]
,3 AS [OrdinalC]
,4 AS [OrdinalD]
FROM
ColTable
) AS d
UNPIVOT
(
ColInfo FOR ColInfos IN (Col_A, Col_B, Col_C, Col_D)
) AS l
UNPIVOT
(
Ordinal FOR Ordinals IN (OrdinalA, OrdinalB, OrdinalC, OrdinalD)
) AS o
WHERE
RIGHT(ColInfos,1) = RIGHT(Ordinals,1)
DROP TABLE ColTable
I have a dynamic single row Table like:
PersonId|FirstName|LastName|Address|PhoneNumber
-----------------------------------------------
1 Anuj Tamrakar NY +525418
I want to pivot this table and want an output in temp table like:
PersonalDetails|Value
----------------------
PersonId 1
FirstName Anuj
LastName Tamrakar
Address NY
PhoneNumber +525418
The first Table is a dynamic single row temp table. For this example, I have 5 columns. I may have more or less columns depending on my criteria
You actually want to UNPIVOT:
SELECT PersonalDetails, Value
FROM
(SELECT CAST([PersonId] AS VARCHAR(MAX)) AS [PersonId],
CAST([FirstName] AS VARCHAR(MAX)) AS [FirstName],
CAST([LastName] AS VARCHAR(MAX)) AS [LastName],
CAST([Address] AS VARCHAR(MAX)) AS [Address],
CAST([PhoneNumber] AS VARCHAR(MAX)) AS [PhoneNumber]
FROM mytable) p
UNPIVOT
(Value FOR PersonalDetails IN
([PersonId], [FirstName], [LastName], [Address], [PhoneNumber])
) AS unpvt;
All 'to-be-unpivoted' fields have to be of the same type, hence the use of CAST.
Demo here
For a dynamic number of columns you have to use dynamic sql:
DECLARE #cols VARCHAR(MAX) = ''
DECLARE #cast_cols VARCHAR(MAX) = ''
DECLARE #qry VARCHAR(MAX)
SELECT #cols = #cols + ',[' + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'mytable' AND TABLE_SCHEMA='dbo'
SELECT #cast_cols = #cast_cols + ',CAST([' + COLUMN_NAME + '] AS VARCHAR(MAX)) AS [' + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'mytable' AND TABLE_SCHEMA='dbo'
SET #cols = STUFF(#cols, 1, 1, '')
SET #cast_cols = STUFF(#cast_cols, 1, 1, '')
SET #qry = 'SELECT PersonalDetails, Value FROM ('
+ #cast_cols +
'FROM mytable) p
UNPIVOT
(Value FOR PersonalDetails IN (' + #cols + ')
) AS unpvt'
EXEC (#qry)
If you really have a single row in the original table, then you can use a series of UNION operations to get your output:
SELECT 'PersonId' AS PersonalDetails, PersonId AS Value
FROM yourTable
UNION ALL
SELECT 'FirstName' AS PersonalDetails, FirstName AS Value
FROM yourTable
UNION ALL
SELECT 'LastName' AS PersonalDetails, LastName AS Value
FROM yourTable
UNION ALL
SELECT 'Address' AS PersonalDetails, Address AS Value
FROM yourTable
UNION ALL
SELECT 'PhoneNumber' AS PersonalDetails, PhoneNumber AS Value
FROM yourTable
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('dbo.yourTableName')
This gives you your column names. You simply insert the column names in your new table and use ``, insert into temp(PersonalDetails, Value) values(column1,select column1 from SingleRowTable