Sql query for Transpose of a dynamic Table - sql

The sql table is as follows,
Name Salary NoticePeriod CTC
Jack 1520 15 123
Bruce 1423 35 165
and it contains about 1000 rows.
I need to do a transpose of this table ,such that the expected output is
Fields Jack Bruce Mike ..... Sam
Salary 1520 1423 235 .. 561
NoticePeriod 15 35 23 253
CTC 123 165 45 ... 125
I tried using Pivot and Unpivot function in Sql Server 2008 . But Since the Name record is large , Pivot query doesnt helps.
My sql attempt is follows,
SELECT *
FROM (
SELECT NAME,
Salary,
NoticePeriod,
CTC
FROM CTCTABLE WITH (NOLOCK)
) AS queryTable
UNPIVOT(Value FOR NAME IN (NAME, Salary, NoticePeriod, CTC)) AS unpv
PIvot(max(Value) FOR NAME IN (Salary, NoticePeriod, CTC)) pv

In your case, you should unpivot the columns Salary, NoticePeriod, CTC into rows, then PIVOT:
WITH Unpivoted
AS
(
SELECT Name, Fields, SalaryValue
FROM salaries AS s
UNPIVOT
(
SalaryValue
FOR Fields IN(Salary, NoticePeriod, CTC)
) AS u
)
SELECT Fields, jack,bruce
FROM Unpivoted AS u
PIVOT
(
MAX(SalaryValue)
FOR Name IN(Jack, Bruce)
) AS p;
The UNPIVOT will transform the columns Salary, NoticePeriod, CTC into values:
Then the pivot will pivot the salary values for each field value and transform the names into columns.
And of course you have to do it dynamically instead of writing list of names like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #colnames AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT #cols = STUFF((SELECT distinct ',' +
QUOTENAME(name)
FROM salaries
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = 'WITH Unpivoted
AS
(
SELECT Name, Fields, SalaryValue
FROM salaries AS s
UNPIVOT
(
SalaryValue
FOR Fields IN(Salary, NoticePeriod, CTC)
) AS u
)
SELECT Fields, ' + #cols + '
FROM Unpivoted AS u
PIVOT
(
MAX(SalaryValue)
FOR Name IN(' + #cols + ')' +
') p';
execute(#query);
This will give you:
Live Demo (Thanks to #lad2025 for it)

Related

Pivoting a dynamic column based on split value

I Have a couple of tables as per below
Position table
Id
PositionName
1
Developer
2
Analyst
3
Tester
Employee table
Id
Name
Positions
1
John
1,2
2
Lisa
3
3
Smith
1
4
Willow
NULL
5
Burly
2,3
From the above tables, what is the query to produce a pivoted report as per below?
Id
Name
Developer
Analyst
Tester
1
John
Y
Y
N
2
Lisa
N
N
Y
3
Smith
Y
N
N
4
Willow
N
N
N
5
Burly
N
Y
Y
I am stuck with the fact I have to do some split string and use the CASE WHEN to apply Y or N to the pivot.
here's my playground in SQL fiddle http://sqlfiddle.com/#!18/2ad8d/31
I'm having trouble getting Fiddle to work right now, but the code isn't too bad to just reproduce in full. Note that you should watch your #Cols variable to make sure all of your position names work as column names in real life, that's often not the case! Also note that your original example had duplicate employee IDs, I gave them unique values.
CREATE table Position (
Id int,
Name varchar(10)
);
insert into Position values
(1, 'Developer'),
(2, 'Analyist'),
(3, 'Tester');
CREATE table Employee (
Id int,
Name varchar(10),
Position varchar(MAX)
);
insert into Employee values
(1, 'John', '1,3'),
(2, 'Lisa', '3'),
(3, 'Smith', '1'),
(4, 'Willow', NULL),
(5, 'Burly', '2,3');
--This is your basic working PIVOT, we'll implement as a dynamic query once we're satisfied it works
;with cteEmp as (
SELECT E.Id as EID, E.Name as EName, P.Name as PName
, CASE WHEN CHARINDEX(CONVERT(nvarchar(10), P.Id)
, CONCAT(',', E.Position, ',') ) > 0 THEN 'Y' ELSE 'N' END as HasPos
FROM Employee as E CROSS JOIN Position as P
)SELECT Piv.* FROM cteEmp as E PIVOT (max(HasPos) FOR PName in (Developer, Analyist, Tester)) as Piv;
--To make it dynamic, build the list of positions from a query
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(Name)
FROM Position
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'with cteEmp as (
SELECT E.Id as EID, E.Name as EName, P.Name as PName
, CASE WHEN CHARINDEX(CONVERT(nvarchar(10), P.Id)
--, CONCAT('','', E.Position, '','') ) > 0 --Not 2008R2!
, '','' + E.Position + '','' ) > 0
THEN ''Y'' ELSE ''N'' END as HasPos
FROM Employee as E CROSS JOIN Position as P
)SELECT Piv.* FROM cteEmp as E PIVOT (max(HasPos)
FOR PName in (' + #cols + ') ) as Piv;'
execute(#query)
EDIT: Fixed the missing close parenthesis in the dynamic query.
EDIT: Note: This does not use a split function, it takes advantage of the facts that the ID must be an integer and the positions listed are delimited in a predictable way. We don't need a list of position IDs, we only need to know if the position ID in question is in the list. We prepend and append commas so we can search on ",1," and not just "1" because "1" would also match "21" but ",1," only matches the single ID.
You just need to join Position table to get the name of the position
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(Name)
FROM Position
FOR XML PATH(''), TYPE ).value('text()[1]', 'NVARCHAR(MAX)')
,1,1,'');
set #query = N'
SELECT Name,' + #cols + N'
from
(
SELECT e.Name, p.PositionName
FROM Employee AS e
CROSS APPLY dbo.SplitString(Position, '','') AS ep(PositionId)
JOIN Position AS p ON p.Id = ep.PositionId
) x
pivot
(
COUNT(*)
FOR p.PositionName IN (' + #cols + N')
) p
';
exec sp_executesql #query;

How to separate duplicate values for a specific row to another column - SQL Server 2008

I have a select statement which checks for a name and a score from two separated tables.
After select operation, I want second scores to be in another column for the same name.
Consider Score 69 will be next to 64 in another column.
How can I do that?
Name Score
John 64
Lisa 45
Jack 23
John 69
Requested:
Name Score Score2
John 64 69
For a dynamic number of result columns, you'll almost certainly need to resort to dynamic SQL. Here's a take on this adapted from Bluefeet's answer here
DECLARE
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(CAST(ScoreNum AS VARCHAR))
FROM
(
SELECT Name, Score, ROW_NUMBER() OVER (Partition By Name ORDER BY Score ASC) AS ScoreNum
FROM NameScore
) scores
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #query = 'SELECT Name, ' + #Cols
+ '
FROM
(
SELECT Name, Score, ROW_NUMBER() OVER (Partition By Name ORDER BY Score ASC) AS ScoreNum
FROM NameScore
) scores
PIVOT
(
MIN(Score)
FOR ScoreNum IN (' + #cols + ')
) AS p';
execute(#query);
With a sample SqlFiddle here
Noted that we had to run the query twice - once to see how many columns to create, and then again for the actual Pivot. For large amounts of data, you could DRY this up into a #Temp table #TableVar.

Pivot a resultset (rows to columns)

Consider the following #table:
guid field value
--------------------
123 A foo
123 B foobar
123 C 123
234 A bar;baz
234 B 3464
345 A foobaz
I need to transpose / pivot this into the following #table2:
guid A B C
---------------------------
123 foo foobar 123
234 bar;baz 3464 -
345 foobaz - -
In other words: the guid needs to stay the table key but all the fields need to be transposed into columns.
Is this possible in SQL Server?
Usually, I would use a server-side (php, python, asp) script to pull #table from the database and transpose the resultset by iterating to the resultset as an array but this is not an option in this case. I need a sql-only solution.
Any help would be greatly appreciated.
This should pivot your data and will keep your GUID.
select tbl.guid, tbl.[A], tbl.[B], tbl.[C] from (
select * from (
select guid, field, value
from #table
) t
pivot (
max(value) for field in ([A],[B],[C])
) p
) tbl
Creating your columns in preparation for the dynamic SQL:
declare #columns nvarchar(max) = (select stuff((
select distinct ',[' + t.field + ']'
from #table t
for xml path('')
),1,1,''))
Mixing the columns into the dynamic SQL:
declare #sql = N'
select tbl.guid, ' + #columns + ' from (
select * from (
select guid, field, value
from #table
) t
pivot (
max(value) for field in (' + #columns + ')
) p
) tbl'
And execute:
execute (#sql)

Distinct Row values as Columns Sql Server

I have a temp table with 3 Columns Like below,
JobID JobType JobValue
12 HR Jesica
23 MANAGER Ravi
5 MANAGER Jacob
60 EMPLOYEE Kiruan
45 MANAGER Abidam
27 HR Kamsura
21 MANAGER Chio Bin
87 EMPLOYEE Gamanya
22 HR Pradeep
56 HR Hari
67 EMPLOYEE Om
14 MANAGER Kiran
My result table should be like
JobID HR MANAGER EMPLOYEE
12
23
5
60
45
27
21
87
22
56
67
14
Jobvalue column values should come into result set.
I have tried like below.
Created a temp table with distict Jobtype row values.
then using while loop inseted JobValue column values into that table.
But it looks very dirty procedure.
Can any one give me a good suggesion to complete this.
Thanks,
You should be able to use the PIVOT function to get the result:
select jobid, hr, manager, employee
from yourtable
pivot
(
max(jobvalue)
for jobtype in (hr, manager, employee)
) piv;
See SQL Fiddle with Demo.
If you want to list the jobvalue under each jobType without showing the jobid, then you could use:
select hr, manager, employee
from
(
select jobtype, jobvalue,
row_number() over(partition by jobtype order by jobid) rn
from yourtable
) d
pivot
(
max(jobvalue)
for jobtype in (hr, manager, employee)
) piv;
See SQL Fiddle with Demo
Try this
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(jobtype)
from yourtable
group by jobtype
ORDER BY jobtype
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--SELECT #cols
set #query = 'SELECT JobID,' + #cols + ' from
(
select JobID, jobtype, jobvalue from yourtable
) x
pivot
(
MAX(jobvalue)
for jobtype in (' + #cols + ')
) p '
execute(#query)

pivoting rows to columns in tsql

I have the following table with the following sample data
ID Language Question SubQuestion SubSubQuestion TotalCount TotalPercent
3 E 9 0 1 88527 73%
3 E 9 0 2 19684 16%
3 E 9 0 3 12960 11%
3 E 9 0 9 933 1%
I want all in one row like this
ID Language TotalCount901 TotalPercent901 TotalCount902 TotalPercent902 TotalCount903 TotalPercent903
3 E 88527 73% 19684 16% 12960 11%
I've tired using the pivot command, but it dosnt to work for me.
I made a few assumptions based on your column names, but it looks like you want to use something similar to this. This applies both an UNPIVOT and then a PIVOT to get the values in the columns you requested:
select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in ([TotalCount901], [totalPercent901],
[TotalCount902], [totalPercent902],
[TotalCount903], [totalPercent903],
[TotalCount909], [totalPercent909])
) p
See SQL Fiddle with Demo
Note: when performing the UNPIVOT the columns need to be of the same datatype. If they are not, then you will need to convert/cast to get the datatypes the same.
If you have an unknown number of values to transform, you can use dynamic sql:
DECLARE #query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsPivot
= STUFF((SELECT ','
+ QUOTENAME(c.name +
cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)))
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name in ('TotalCount', 'TotalPercent')
group by c.name, t.question, t.subquestion, t.subsubquestion
order by t.SubSubQuestion
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in (' + #colsPivot + ')
) p '
execute(#query)
See SQL Fiddle with Demo