I want to transpose the below rows dynamically into columns.
**Process Id Attribute Values**
1 Equipment Normal
1 Complaints No
1 Availability 30 min
2 Phone1 123456789
2 Phone2 987654321
I have tried to pivot it but I am unable to get the desired results. I need the below output
Process ID Attribute1 Value1 Arrtibute2 Value2 Attribute3 Value3
1 Equipment Normal Complaints No Availability 30 min
2 Phone1 123456789 Phone2 987654321 NULL NULL
One process can have one or more attributes. So if a process have 10 attributes, it should create 10 columns in the desired output. Any suggestions?
All the newest ANSI compliant databases should support this. Witn no DBMS, I stick to the newest ANSI standard.
WITH
-- your input
input(Process_Id,Attribute,Values) AS (
SELECT 1,'Equipment','Normal'
UNION ALL SELECT 1,'Complaints','No'
UNION ALL SELECT 1,'Availability','30 min'
UNION ALL SELECT 2,'Phone1','123456789'
UNION ALL SELECT 2,'Phone2','987654321'
)
,
-- need a sequence counter ...
with_seq AS (
SELECT
ROW_NUMBER() OVER(PARTITION BY process_id) AS seq
, *
FROM input
)
SELECT
process_id
, MAX(CASE seq WHEN 1 THEN attribute END) AS attrib1
, MAX(CASE seq WHEN 1 THEN values END) AS val1
, MAX(CASE seq WHEN 2 THEN attribute END) AS attrib2
, MAX(CASE seq WHEN 2 THEN values END) AS val2
, MAX(CASE seq WHEN 3 THEN attribute END) AS attrib3
, MAX(CASE seq WHEN 3 THEN values END) AS val3
FROM with_seq
GROUP BY process_id;
-- out process_id | attrib1 | val1 | attrib2 | val2 | attrib3 | val3
-- out ------------+-----------+-----------+------------+-----------+--------------+-------
-- out 1 | Equipment | Normal | Complaints | No | Availability | 30 min
-- out 2 | Phone1 | 123456789 | Phone2 | 987654321 | |
Try this below code. It will provide desired output.
My sample Algorithm
1. Create manual named columns by using ROW_NUMBER()
2. Create Dynamic columns for Attribute,Value named as #columnsAttribute, #columnsValue
3. Create Dynamic group by columns for both Attribute & Value named as #Allcolumns
4. Dynamic query creation with pivot for both Attribute & Value
declare #tblAttribute as
table(processid int,
attribute NVARCHAR(100),
value NVARCHAR(200))
insert into #tblAttribute(processid,attribute,value)
values(1,'Equipment','Normal'),
(1,'Complaints','No'),
(1,'Availability','30 min'),
(1,'test','testvalue'),
(2,'Phone1','123456789'),
(2,'Phone2','987654321')
;with ctetbl as
(
select ROW_NUMBER() over (partition by processid order by processid) rno,* from #tblAttribute
)
,ctetbl1 as
(
select processid,
'Attribute'+ cast(rno as NVARCHAR(2)) as DynamicAttribute,
'Value'+cast(rno as NVARCHAR(2)) as DyanamicValue
,attribute,value from ctetbl
)
select * into #tblDynamicAttribute from ctetbl1
declare #Allcolumns as NVARCHAR(max),
#columnsAttribute as NVARCHAR(max),
#columnsValue as NVARCHAR(max),
#sql as NVARCHAR(MAX) = ''
select #Allcolumns=coalesce(#Allcolumns+',','')+'max('+QUOTENAME(B.DynamicAttribute)+') as '+ QUOTENAME(B.DynamicAttribute) +',max('+QUOTENAME(B.DyanamicValue)+') as '+ QUOTENAME(B.DyanamicValue)
from (select distinct DynamicAttribute,DyanamicValue from #tblDynamicAttribute) as B
order by b.DynamicAttribute
select #columnsAttribute=coalesce(#columnsAttribute+',','')+QUOTENAME(B.DynamicAttribute)
from (select distinct DynamicAttribute,DyanamicValue from #tblDynamicAttribute) as B
order by b.DynamicAttribute
select #columnsValue=coalesce(#columnsValue+',','')+QUOTENAME(B.DyanamicValue)
from (select distinct DynamicAttribute,DyanamicValue from #tblDynamicAttribute) as B
order by b.DynamicAttribute
-- construct dynamic SQL
SET #sql ='
select x.processid,' + #Allcolumns +' from (
SELECT processid,' + #columnsAttribute+','+ #columnsValue +' FROM
(
SELECT
processid,attribute,value,dynamicattribute,DyanamicValue
FROM
#tblDynamicAttribute p
) t
PIVOT(
max(attribute)
FOR dynamicattribute IN ('+ #columnsAttribute +')
) AS pivot_table
PIVOT(
max(value)
FOR DyanamicValue IN ('+ #columnsValue +')
) AS pivot_table1
) x group by processid;';
-- execute the dynamic SQL
EXECUTE sp_executesql #sql;
drop table #tblDynamicAttribute
Sample Output
Related
I have a query like below:
DECLARE #t TABLE
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
--SELECT * FROM #t
SELECT
EmpName, [Dog], [Cat], [Goat], [Ram]
FROM
(SELECT
EmpName, Qty, Item
FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN ([Dog], [Cat], [Goat], [Ram])) AS p
And the result is as seen in the screenshot below:
I want to calculate the average Qty across Item without ignoring null values in the calculation. For example, in row 1, EmpName Abay should be 5 divided by 4 (number of columns), as seen in this screenshot:
How do I get the average column?
I'm not really familiar with the PIVOT query, so here is an alternative using conditional aggregation:
SELECT
Empname,
Dog = SUM(CASE WHEN Item = 'Dog' THEN Qty ELSE 0 END),
Cat = SUM(CASE WHEN Item = 'Cat' THEN Qty ELSE 0 END),
Goat = SUM(CASE WHEN Item = 'Goat' THEN Qty ELSE 0 END),
Ram = SUM(CASE WHEN Item = 'Ram' THEN Qty ELSE 0 END),
Average = SUM(ISNULL(Qty, 0))/ 4.0
FROM #t
GROUP BY EmpName;
Note that this will only work if you only have 4 Items. Otherwise, you need to resort to dynamic crosstab.
ONLINE DEMO
For dynamic crosstab, I used a temporary table instead of a table variable:
DECLARE #sql NVARCHAR(MAX) = '';
SELECT #sql =
'SELECT
Empname' + CHAR(10);
SELECT #sql = #sql +
' , SUM(CASE WHEN Item = ''' + Item + ''' THEN Qty ELSE 0 END) AS ' + QUOTENAME(Item) + CHAR(10)
FROM (
SELECT DISTINCT Item FROM #t
) t;
SELECT #sql = #sql +
' , SUM(ISNULL(Qty, 0)) / (SELECT COUNT(DISTINCT Item) * 1.0 FROM #t) AS [Average]' + CHAR(10) +
'FROM #t
GROUP BY EmpName;';
ONLINE DEMO
Try a combination of AVG and ISNULL, i.e. AVG(ISNULL(Dog, 0)).
One simple method is:
select empname, goat, cat, dog, ram,
(coalesce(goat, 0) + coalesce(cat, 0) + coalesce(dog, 0) + coalesce( ram, 0)
) / 4.0 as average
from t;
Another simple method uses outer apply:
select t.*, v.average
from t outer apply
(select avg(coalesce(x, 0))
from (values (t.goat), (t.cat), (t.dog), (t.ram)
) v(x)
) v(average);
DECLARE #t TABLE
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
SELECT EmpName
, [Dog]
, [Cat]
, [Goat]
, [Ram]
,p.total/4.0 as av
FROM (SELECT EmpName, Qty, Item,SUM(qty)OVER(PARTITION BY EmpName) AS total FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
EmpName Dog Cat Goat Ram av
---------- ----------- ----------- ----------- ----------- ---------------------------------------
Abay NULL NULL 5 NULL 1.250000
Carle 2 11 NULL NULL 3.250000
Jane 6 NULL 8 3 4.250000
V2: Dynamic script:
CREATE TABLE #t
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
INSERT #t ( EmpName, Qty, Item )VALUES('Abay',100,'abc')
DECLARE #cols VARCHAR(max),#sql VARCHAR(MAX),#cnt INT
SELECT #cols=ISNULL(#cols+',[','[')+Item+']',#cnt=ISNULL(#cnt+1,1) FROM #t GROUP BY Item
PRINT #cols
PRINT #cnt
SET #sql='SELECT EmpName, '+#cols+',p.total*1.0/'+LTRIM(#cnt)+' as av'+CHAR(13)
+' FROM (SELECT EmpName, Qty, Item,SUM(qty)OVER(PARTITION BY EmpName) AS total FROM #t) AS b'+CHAR(13)
+' PIVOT(SUM(Qty) FOR Item IN('+#cols+')) AS p'
EXEC(#sql)
EmpName abc Cat Dog Goat Ram av
---------- ----------- ----------- ----------- ----------- ----------- ---------------------------------------
Carle NULL 11 2 NULL NULL 2.600000
Jane NULL NULL 6 8 3 3.400000
Abay 100 NULL NULL 5 NULL 21.000000
Avoid NULL from your pivot sentence and compute AVG.
;with ct as
(
SELECT EmpName
, ISnull([Dog],0) Dog
, ISnull([Cat],0) Cat
, ISnull([Goat],0) Goat
, ISnull([Ram],0) Ram
FROM (SELECT EmpName, Qty, Item FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
)
select empname, avg(dog) dog, avg(cat) cat, avg(goat) goat, avg(ram) ram
from ct
group by empname;
+---------+-----+-----+------+-----+
| empname | dog | cat | goat | ram |
+---------+-----+-----+------+-----+
| Abay | 0 | 0 | 5 | 0 |
+---------+-----+-----+------+-----+
| Carle | 2 | 11 | 0 | 0 |
+---------+-----+-----+------+-----+
| Jane | 6 | 0 | 8 | 3 |
+---------+-----+-----+------+-----+
SELECT EmpName
, [Dog]
, [Cat]
, [Goat]
, [Ram]
,(isnull(p.cat,0)+isnull(p.dog,0)+isnull(p.Goat,0)+isnull(p.Ram,0))/4.0 as average
FROM (SELECT EmpName, Qty, Item FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
FYI, this question is already answered but I have some new requirements which is very complex to implement so, I am posting it as a new question instead of editing the old question: (Previous Question)
I have two tables "Controls" and "ControlChilds" (in the ControlChilds table we have added a new column called ControlChildComments which we need to show in PIVOT output)
Parent Table Structure:
Create table Controls(
ProjectID Varchar(20) NOT NULL,
ControlID INT NOT NULL,
ControlCode Varchar(2) NOT NULL,
ControlPoint Decimal NULL,
ControlScore Decimal NULL,
ControlValue Varchar(50)
)
Sample Data
ProjectID | ControlID | ControlCode | ControlPoint | ControlScore | ControlValue
P001 1 A 30.44 65 Invalid
P001 2 C 45.30 85 Valid
Child Table Structure:
Create table ControlChilds(
ControlID INT NOT NULL,
ControlChildID INT NOT NULL,
ControlChildValue Varchar(200) NULL,
ControlChildComments Varchar(200) NULL
)
Sample Data
ControlID | ControlChildID | ControlChildValue | ControlChildComments
1 100 Yes Something
1 101 No NULL
1 102 NA Others
1 103 Others NULL
2 104 Yes New one
2 105 SomeValue NULL
Based on my previous question (Previous Question) I got this output (You can refer to the PIVOT queries which produces this output in the answer given by #bluefeet. Thanks again #bluefeet.)
But now my requirement is changed and I need ControlChildComments after each Child values. For example, A_Child1, A_Child1Comments, A_Child2, A_Child2Comments etc...
Another tricky thing is I need to show the comments only when they are not null otherwise I shouldn't show the column. For example, in this case, it should be like this:
A_Child1, A_Child1Comments, A_Child2, A_Child3, A_Child3Comments, A_Child4, C_Child1, C_Child1Comments, C_Child2
Is this possible? I tried lot of things but the results are not accurate.
Since you now have multiple columns in your ControlChilds table that you need to PIVOT, you will need to use the similar method of unpivoting them first that you applied with the Controls table.
You will need to unpivot both the ChildControlValue and ChildControlComments using code similar to:
select
projectId,
col = ControlCode+'_'+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', ControlChildValue union all
select 'ChildComments', ControlChildComments
) c (subCol, value);
See SQL Fiddle with Demo. This gets your data in the format:
| PROJECTID | COL | VALUE |
|-----------|------------------|-----------|
| P001 | A_ChildValue1 | Yes |
| P001 | A_ChildComments1 | Something |
| P001 | A_ChildValue2 | No |
| P001 | A_ChildComments2 | (null) |
| P001 | A_ChildValue3 | NA |
You then use this code in your existing query:
select ProjectId,
A_ControlPoint, A_ControlScore, A_ControlValue,
A_ChildValue1, A_ChildComments1, A_ChildValue2,
A_ChildComments2, A_ChildValue3, A_ChildComments3,
A_ChildValue4, A_ChildComments4,
C_ControlPoint, C_ControlScore, C_ControlValue,
C_Child1, C_Child2
from
(
select
ProjectId,
col = ControlCode +'_'+col,
val
from
(
select
c.ProjectId,
c.ControlCode,
c.ControlPoint,
c.ControlScore,
c.ControlValue
from controls c
) d
cross apply
(
select 'ControlPoint', cast(controlpoint as varchar(10)) union all
select 'ControlScore', cast(ControlScore as varchar(10)) union all
select 'ControlValue', ControlValue
) c (col, val)
union all
select
projectId,
col = ControlCode+'_'+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', ControlChildValue union all
select 'ChildComments', ControlChildComments
) c (subCol, value)
) src
pivot
(
max(val)
for col in (A_ControlPoint, A_ControlScore, A_ControlValue,
A_ChildValue1, A_ChildComments1, A_ChildValue2,
A_ChildComments2, A_ChildValue3, A_ChildComments3,
A_ChildValue4, A_ChildComments4,
C_ControlPoint, C_ControlScore, C_ControlValue,
C_Child1, C_Child2)
) piv;
See SQL Fiddle with Demo. Finally, you'll implement this in your dynamic SQL script:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col)
from
(
select ControlCode,
col = ControlCode +'_'+col,
seq,
so
from controls
cross apply
(
select 'ControlPoint', 0, 0 union all
select 'ControlScore', 0, 1 union all
select 'ControlValue', 0, 2
) c (col, seq, so)
union all
select ControlCode,
col = ControlCode+'_'+subcol+cast(rn as varchar(10)),
rn,
so
from
(
select ControlCode,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', seq, 3 union all
select 'ChildComments', seq, 4
) c (subcol, rn, so)
) src
group by ControlCode, seq, col, so
order by ControlCode, seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ProjectId, ' + #cols + '
from
(
select ProjectId,
col = ControlCode +''_''+col,
val
from
(
select
c.ProjectId,
c.ControlCode,
c.ControlPoint,
c.ControlScore,
c.ControlValue
from controls c
) d
cross apply
(
select ''ControlPoint'', cast(controlpoint as varchar(10)) union all
select ''ControlScore'', cast(ControlScore as varchar(10)) union all
select ''ControlValue'', ControlValue
) c (col, val)
union all
select
projectId,
col = ControlCode+''_''+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select ''ChildValue'', ControlChildValue union all
select ''ChildComments'', ControlChildComments
) c (subCol, value)
) x
pivot
(
max(val)
for col in (' + #cols + ')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. Both of these gives a result:
| PROJECTID | A_CONTROLPOINT | A_CONTROLSCORE | A_CONTROLVALUE | A_CHILDVALUE1 | A_CHILDCOMMENTS1 | A_CHILDVALUE2 | A_CHILDCOMMENTS2 | A_CHILDVALUE3 | A_CHILDCOMMENTS3 | A_CHILDVALUE4 | A_CHILDCOMMENTS4 | C_CONTROLPOINT | C_CONTROLSCORE | C_CONTROLVALUE | C_CHILDVALUE1 | C_CHILDCOMMENTS1 | C_CHILDVALUE2 | C_CHILDCOMMENTS2 |
|-----------|----------------|----------------|----------------|---------------|------------------|---------------|------------------|---------------|------------------|---------------|------------------|----------------|----------------|----------------|---------------|------------------|---------------|------------------|
| P001 | 30.44 | 65.00 | Invalid | Yes | Something | No | (null) | NA | Others | Others | (null) | 45.30 | 85.00 | Valid | Yes | New one | SomeValue | (null) |
Here is an example of a dynamic crosstab. Since you have multiple columns you would need to adjust the dynamic portion of this to suit.
if OBJECT_ID('Something') is not null
drop table Something
create table Something
(
ID int,
Subject1 varchar(50)
)
insert Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20' union all
select 12345, 'asdfasdf'
declare #MaxCols int
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by ID order by ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Subject1 end) as Subject' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from Something
group by ID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
--declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
--exec sp_executesql #SqlToExecute
First of all, I am new to SQL. Here is the sample (for both table1 and table2, I have created a SNO as primary key and it's also identity column)
Table1:
PID PNAME PartID
--- ----- ------
0 Length 1
1 Breadth 1
2 Height 1
0 Area 2
1 Volume 2
Table2:
SampleID PID Pvalue PartID ModifiedDate Operator
-------- --- ------ ------ ------------ --------
0 0 10 1 10-Mar-14 Test
0 1 10 1 10-Mar-14 Test
0 2 Fail 1 10-Mar-14 Test
1 0 20 1 12-Mar-14 Test
1 1 Fail 1 12-Mar-14 Test
1 2 Fail 1 12-Mar-14 Test
0 0 10 2 13-Mar-14 Test1
0 1 10 2 13-Mar-14 Test1
Depending upon the PartID, I must get the following results
PARTID: 1
PNAME 0 1
------------ --------- ---------
Length 10 20
Breadth 10 Fail
Height Fail Fail
ModifiedDate 10-Mar-14 12-Mar-14
Operator Test Test
PARTID: 2
PNAME 0
------------ ---------
Area 10
Volume 10
ModifiedDate 13-Mar-14
Operator Test1
How to achieve the desired output as mentioned above in SQL Server 2008?
You can use PIVOT to get the result but you will also need to unpivot the ModifiedDate and Operator columns so you can display them in a single column with the PName. Your final result will need a dynamic solution but it would be much easier to write this static first, then convert to dynamic sql.
The basic syntax will be:
select pname, [0], [1]
from
(
select t2.sampleid, pname = c.col, c.value
from table1 t1
inner join table2 t2
on t1.partid = t2.partid
and t1.pid = t2.pid
cross apply
(
select Pname, pvalue union all
select 'ModifiedDate', convert(varchar(10), ModifiedDate, 120) union all
select 'Operator', Operator
) c (col, value)
where t1.partid = 1
) d
pivot
(
max(value)
for sampleid in ([0], [1])
) p;
See SQL Fiddle with Demo. You'll see that I used CROSS APPLY to convert the 3 columns PName, ModifiedDate and Operator into a single column. This is necessary so you can easily get to the values for each SampleId. The above version is a static version meaning you are hard-coding the values for the final columns, but if you want to have this adjust based on the PartId, you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#partid int,
#paramdef nvarchar(max)
set #partid = 1
set #paramdef = '#partid int'
select #cols = STUFF((SELECT ',' + QUOTENAME(sampleid)
from Table2
where partid = #partid
group by sampleid
order by sampleid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT pname,' + #cols + '
from
(
select t2.sampleid, pname = c.col, c.value
from table1 t1
inner join table2 t2
on t1.partid = t2.partid
and t1.pid = t2.pid
cross apply
(
select Pname, pvalue union all
select ''ModifiedDate'', convert(varchar(10), ModifiedDate, 120) union all
select ''Operator'', Operator
) c (col, value)
where t1.partid = #partid
) x
pivot
(
max(value)
for sampleid in (' + #cols + ')
) p '
exec sp_executesql #query, #paramdef, #partid = #partid;
See SQL Fiddle with Demo. Both give a result:
| PNAME | 0 | 1 |
|--------------|------------|------------|
| Breadth | 10 | Fail |
| Height | Fail | Fail |
| Length | 10 | 20 |
| ModifiedDate | 2014-03-10 | 2014-03-12 |
| Operator | Test | Test |
I have the following table and data:
CREATE TABLE SourceTbl ([Code] varchar(3), [Total] decimal, [Date] datetime );
INSERT INTO SourceTbl ([Code], [Total], [Date])
VALUES ('AA', 100, '2012-12-01'), ('AA', 200, '2013-02-01'), ('BB', 50, '2012-01-01');
A simple select will return
Code | Total | Date
'AA' | 100 | 2012-12-01
'AA' | 200 | 2013-02-01
'BB' | 50 | 2012-01-01
but what I need is the following
Code | Total | Date | Total | Date
'AA | 200 | 2013-02-01 | 100 | 2012-12-01
'BB | 50 | 2012-01-01 | null | null
I have been trying to do this using a PIVOT operator but without success (based on the question SQL Server Pivot multiple columns based on one column).
Using that example, all I get are two rows with null values.
The Total/Date columns can be repeated 13 times and they must be ordered by Date DESC.
SQL Fiddle: http://sqlfiddle.com/#!3/f37a1/2
Any help is appreciated!
Thanks!
If you need just two columns:
with cte as (
select *, row_number() over(partition by Code order by Date) as rn
from SourceTbl
)
select
code,
max(case when rn = 1 then Total end) as Total1,
max(case when rn = 1 then Date end) as Date1,
max(case when rn = 2 then Total end) as Total2,
max(case when rn = 2 then Date end) as Date2
from cte
group by code
=> sql fiddle demo
dynamic solution:
declare #stmt nvarchar(max)
;with cte as (
select distinct
cast(row_number() over(partition by Code order by Date) as nvarchar(max)) as rn
from SourceTbl
)
select #stmt = isnull(#stmt + ', ', '') +
'max(case when rn = ' + rn + ' then Total end) as Total' + rn + ',' +
'max(case when rn = ' + rn + ' then Date end) as Date' + rn
from cte
order by rn
select #stmt = '
with cte as (
select *, row_number() over(partition by Code order by Date) as rn
from SourceTbl
)
select
code, ' + #stmt + ' from cte group by code'
exec sp_executesql
#stmt = #stmt
=> sql fiddle demo
Are you trying to dynamically create columns in your result set?
If you had a third record of 'AA' with a total of 300 and the date of 03/01/2013 would you that mean you would want something like this displayed?
Code | Total | Date | Total | Date | Total | Date
AA | 200 | 2013-02-01 | 100 | 2012-12-01| 300 | 03-01-13
BB | 50 | 2012-01-01 | null | null | null | null
I have to show multiple incomes, type of income and employer name values for a single individual in a single row. So, if 'A' has three different incomes from three different sources,
id | Name | Employer | IncomeType | Amount
123 | XYZ | ABC.Inc | EarningsformJob | $200.00
123 | XYZ | Self | Self Employment | $300.00
123 | XYZ. | ChildSupport| Support | $500.00
I need to show them as
id | Name | Employer1 | Incometype1| Amount1 | Employer2 | incometype2 | Amount2| Employer3 | Incometype3| Amount3.....
123 |XYZ | ABC.Inc |EarningsformJob | $200.00|Self | Self Employment | $300.00|ChildSupport| Support | $500.00.....
I need both 'fixed number of columns' (where we know how many times employer, incometype and amount colums are going to repeat)logic and 'dynamic display of columns' ( unknown number of times these columns are going to repeat)
Thanks.
Since you are using SQL Server there are several ways that you can transpose the rows of data into columns.
Aggregate Function / CASE: You can use an aggregate function with a CASE expression along with row_number(). This version would require that you have a known number of values to become columns:
select id,
name,
max(case when rn = 1 then employer end) employer1,
max(case when rn = 1 then IncomeType end) IncomeType1,
max(case when rn = 1 then Amount end) Amount1,
max(case when rn = 2 then employer end) employer2,
max(case when rn = 2 then IncomeType end) IncomeType2,
max(case when rn = 2 then Amount end) Amount2,
max(case when rn = 3 then employer end) employer3,
max(case when rn = 3 then IncomeType end) IncomeType3,
max(case when rn = 3 then Amount end) Amount3
from
(
select id, name, employer, incometype, amount,
row_number() over(partition by id order by employer) rn
from yourtable
) src
group by id, name;
See SQL Fiddle with Demo.
PIVOT/UNPIVOT: You could use the UNPIVOT and PIVOT functions to get the result. The UNPIVOT converts your multiple columns of Employer, IncomeType and Amount into multiples rows before applying the pivot. You did not specific what version of SQL Server, assuming you have a known number of values then you could use the following in SQL Server 2005+ which uses CROSS APPLY with UNION ALL to unpivot:
select id, name,
employer1, incometype1, amount1,
employer2, incometype2, amount2,
employer3, incometype3, amount3
from
(
select id, name, col+cast(rn as varchar(10)) col, value
from
(
select id, name, employer, incometype, amount,
row_number() over(partition by id order by employer) rn
from yourtable
) t
cross apply
(
select 'employer', employer union all
select 'incometype', incometype union all
select 'amount', cast(amount as varchar(50))
) c (col, value)
) src
pivot
(
max(value)
for col in (employer1, incometype1, amount1,
employer2, incometype2, amount2,
employer3, incometype3, amount3)
) piv;
See SQL Fiddle with Demo.
Dynamic Version: Lastly, if you have an unknown number of values then you will need to use dynamic SQL to generate the result.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(rn as varchar(10)))
from
(
select row_number() over(partition by id order by employer) rn
from yourtable
) d
cross apply
(
select 'employer', 1 union all
select 'incometype', 2 union all
select 'amount', 3
) c (col, so)
group by col, rn, so
order by rn, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, name,' + #cols + '
from
(
select id, name, col+cast(rn as varchar(10)) col, value
from
(
select id, name, employer, incometype, amount,
row_number() over(partition by id order by employer) rn
from yourtable
) t
cross apply
(
select ''employer'', employer union all
select ''incometype'', incometype union all
select ''amount'', cast(amount as varchar(50))
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. All versions give a result:
| ID | NAME | EMPLOYER1 | INCOMETYPE1 | AMOUNT1 | EMPLOYER2 | INCOMETYPE2 | AMOUNT2 | EMPLOYER3 | INCOMETYPE3 | AMOUNT3 |
-------------------------------------------------------------------------------------------------------------------------------------
| 123 | XYZ | ABC.Inc | EarningsformJob | 200 | ChildSupport | Support | 500 | Self | Self Employment | 300 |