How can I return several text fields with the same ID number in one row in SQL server? - sql

I have table:
ID Note
1 1 aaa
2 1 bbb
3 1 ccc
4 2 ddd
5 2 eee
6 2 fff
I need to return it as:
ID Note1 Note2 Note3
1 1 aaa bbb ccc
2 2 ddd eee fff
Thank you!

You can use the PIVOT function for this type of query. If you have a known number of columns, then you can hard-code the values:
select *
from
(
select id, note,
'Note' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in ([Note1], [Note2], [Note3])
) p
See SQL Fiddle with Demo
If you are going to have an unknown number of notes that you want to turn into columns, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ','
+ QUOTENAME('Note' +
cast(row_number() over(partition by id order by id) as varchar(10)))
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id,' + #cols + ' from
(
select id, note,
''Note'' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
Both will produce the same results.
| ID | NOTE1 | NOTE2 | NOTE3 |
------------------------------
| 1 | aaa | bbb | ccc |
| 2 | ddd | eee | fff |
Or if you do not want to use the PIVOT function, then you can use an aggregate function with a CASE statement:
select id,
max(case when rn = 1 then note else '' end) Note1,
max(case when rn = 2 then note else '' end) Note2,
max(case when rn = 3 then note else '' end) Note3
from
(
select id, note,
row_number() over(partition by id order by id) rn
from yourtable
) src
group by id
See SQL Fiddle with Demo

Related

SQL Server: Turning rows into columns

I'm trying to make the following structure
ROW | GROUP | ORDER | COL_NAME | VALUE
---------------------------------------------
1 | 10 | 10 | FIRST_COL | Value1
2 | 10 | 10 | FIRST_COL | Value2
3 | 10 | 10 | FIRST_COL | Value3
4 | 10 | 20 | SECOND_COL| Val1
5 | 10 | 20 | SECOND_COL| Val2
6 | 20 | 10 | THIRD_COL | Opt3
...
into
FIRST_COL | SECOND_COL | THIRD_COL
-----------------------------------------------------
Value1 | Val1 | Opt3
Value2 | Val2 |
Value3 | |
What I currently have:
declare #cols varchar(max),
#query varchar(max)
select #cols = stuff((select ',' + quotename([COL_NAME])
from mt
group by [COL_NAME]
for xml path(''), type).value('.', 'varchar(max)'), 1, 1, '');
set #query = 'select ' + #cols + ' from (
select [COL_NAME], [VALUE]
from mt
) x
pivot (
min([VALUE])
for [COL_NAME] in (' + #cols + ')
) p
';
execute(#query);
The current code only shows the minimum values (since it's set to min([VALUES])), so only Value1, Val1 and Opt3 would be shown, but my question here is, how can I modify the code so I get the appropriate table/view?
Thanks!
I believe you need this:
set #query = 'select ' + #cols + ' from (
select [COL_NAME], [VALUE], ROW_NUMBER() OVER (PARTITION BY [COL_NAME] ORDER BY (SELECT NULL)) as seqnum
from mt
) x
pivot (
min([VALUE])
for [COL_NAME] in (' + #cols + ')
) p
';
The pivot uses all the columns in the subquery. You just need one to distinguish among the rows with the same column name.
Something like this?
Declare #YourTable table ([ROW] int,[GROUP] int,[ORDER] int,[COL_NAME] varchar(50),[VALUE] varchar(50))
Insert Into #YourTable Values
(1,10,10,'FIRST_COL' ,'Value1'),
(2,10,10,'FIRST_COL' ,'Value2'),
(3,10,10,'FIRST_COL' ,'Value3'),
(4,10,20,'SECOND_COL','Val1'),
(5,10,20,'SECOND_COL','Val2'),
(6,20,10,'THIRD_COL' ,'Opt3')
;with cteBase as (
Select *
,RowNr = Row_Number() over (Partition By [COL_NAME] Order by [ROW])
From #YourTable
)
Select First_Col = max(case when [COL_NAME]='FIRST_COL' then Value else '' end)
,Second_Col = max(case when [COL_NAME]='SECOND_COL' then Value else '' end)
,Third_Col = max(case when [COL_NAME]='THIRD_COL' then Value else '' end)
From cteBase
Group By RowNr
Returns
First_Col Second_Col Third_Col
Value1 Val1 Opt3
Value2 Val2
Value3
Try it like this:
DECLARE #tbl TABLE([ROW] INT,[GROUP] INT,[ORDER] INT,[COL_NAME] VARCHAR(100),VALUE VARCHAR(100));
INSERT INTO #tbl VALUES
(1,10,10,'FIRST_COL','Value1')
,(2,10,10,'FIRST_COL','Value2')
,(3,10,10,'FIRST_COL','Value3')
,(4,10,20,'SECOND_COL','Val1')
,(5,10,20,'SECOND_COL','Val2')
,(6,20,10,'THIRD_COL','Opt3');
WITH Sorted AS
(
SELECT tbl.*
,ROW_NUMBER() OVER(PARTITION BY tbl.[COL_NAME] ORDER BY tbl.[ROW]) AS SortInx
FROM #tbl AS tbl
)
SELECT MAX(CASE WHEN Sorted.[COL_NAME]='FIRST_COL' THEN Sorted.[VALUE] END) AS FIRST_COL
,MAX(CASE WHEN Sorted.[COL_NAME]='SECOND_COL' THEN Sorted.[VALUE] END) AS SECOND_COL
,MAX(CASE WHEN Sorted.[COL_NAME]='THIRD_COL' THEN Sorted.[VALUE] END) AS THIRD_COL
FROM Sorted
GROUP BY SortInx
As he stated, you need to use RowNumber() to populate your Row column, on your COL_NAME, so your data will look like this.
ROW | GROUP | ORDER | COL_NAME | VALUE
---------------------------------------------
1 | 10 | 10 | FIRST_COL | Value1
2 | 10 | 10 | FIRST_COL | Value2
3 | 10 | 10 | FIRST_COL | Value3
1 | 10 | 20 | SECOND_COL| Val1
2 | 10 | 20 | SECOND_COL| Val2
1 | 20 | 10 | THIRD_COL | Opt3
...
Then, just include the column in your SQL
set #query = 'select [Row], ' + #cols + ' from (
select [Row],[COL_NAME], [VALUE]
from mt
) x
pivot (
min([VALUE])
for [COL_NAME] in (' + #cols + ')
) p
';
execute(#query);
Then, you have
ROW FIRST_COL SECOND_COL THIRD_COL
1 Value1 Val1 Opt3
2 Value2 Val2 NULL
3 Value3 NULL NULL

extend current query, calculated columns

My table looks for example like this:
Name date result
A 2012-01-01 1
A 2012-02-01 2
B 2013-01-01 1
...
For a full example: http://sqlfiddle.com/#!3/0226b/1
At the moment I have a working query that counts the rows by person and year: http://sqlfiddle.com/#!3/0226b/3
This is perfect, but what I want is some extra information for 2014. i need to count how many rows I have for every result.
something like this:
NAME 1 2 3 2014 2013 2012 TOTAL
Person B 4 0 2 6 2 2 10
Person A 2 1 1 4 3 4 11
Person C 1 1 1 3 1 0 4
Even better would be that I give the result-columns a good name (1 = lost, 2= draw, 3=won):
NAME lost draw won 2014 2013 2012 TOTAL
Person B 4 0 2 6 2 2 10
Person A 2 1 1 4 3 4 11
Person C 1 1 1 3 1 0 4
I tried to add some extra code, like:
select #colsResult
= STUFF((SELECT ',' + QUOTENAME(result)
from list
group by result
order by result
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
I have as result:
,[1]
,[2]
,[3]
But if I run the whole code I get an error, invallid column name...
Since you have two columns that you now want to PIVOT, you'll first have to unpivot those columns and then convert those values into the new columns.
Starting in SQL Server 2005, you could use CROSS APPLY to unpivot the columns. The basic syntax will be similar to:
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select 'dt', dt union all
select 'result', result
) c (old_col_name, new_col)
See SQL Fiddle with Demo. This query gets you a list of names, with the "new columns" and then the Total entries for each name.
| NAME | NEW_COL | TOTAL |
|----------|---------|-------|
| Person A | 2012 | 11 |
| Person A | 1 | 11 |
| Person A | 2012 | 11 |
| Person A | 2 | 11 |
You'll see that the dates and the results are now both stored in "new_col". These values will now be used as the new column names. If you have a limited number of columns, then you would simply hard-code the query:
select name, lost = [1],
draw=[2], won = [3],
[2014], [2013], [2012], Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select 'dt', dt union all
select 'result', result
) c (old_col_name, new_col)
) src
pivot
(
count(new_col)
for new_col in([1], [2], [3], [2014], [2013], [2012])
) piv
order by [2014];
See SQL Fiddle with Demo
Now since your years are dynamic, then you'll need to use dynamic sql. But it appears that you have 3 results and potentially multiple years - so I'd use a combination of static/dynamic sql to make this easier:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#orderby nvarchar(max)
select #cols
= STUFF((SELECT ',' + QUOTENAME(year(date))
from list
group by year(date)
order by year(date) desc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #orderby = 'ORDER BY ['+cast(year(getdate()) as varchar(4)) + '] desc'
set #query = 'SELECT name, lost = [1],
draw=[2], won = [3],' + #cols + ', Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select ''dt'', dt union all
select ''result'', result
) c (old_col_name, new_col)
) x
pivot
(
count(new_col)
for new_col in ([1], [2], [3],' + #cols + ')
) p '+ #orderby
exec sp_executesql #query;
See SQL Fiddle with Demo. This gives a result:
| NAME | LOST | DRAW | WON | 2014 | 2013 | 2012 | TOTAL |
|----------|------|------|-----|------|------|------|-------|
| Person B | 7 | 1 | 2 | 6 | 2 | 2 | 10 |
| Person A | 5 | 3 | 3 | 4 | 3 | 4 | 11 |
| Person C | 2 | 1 | 1 | 3 | 1 | 0 | 4 |
If you want to only filter the result columns for the current year, then you can perform this filtering a variety of ways but the easiest you be to include a filter in the unpivot. The hard-coded version would be:
select name, lost = [1],
draw=[2], won = [3],
[2014], [2013], [2012], Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select 'dt', dt union all
select 'result', case when dt = 2014 then result end
) c (old_col_name, new_col)
) src
pivot
(
count(new_col)
for new_col in([1], [2], [3], [2014], [2013], [2012])
) piv
order by [2014] desc;
See SQL Fiddle with Demo. Then the dynamic sql version would be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#orderby nvarchar(max),
#currentYear varchar(4)
select #currentYear = cast(year(getdate()) as varchar(4))
select #cols
= STUFF((SELECT ',' + QUOTENAME(year(date))
from list
group by year(date)
order by year(date) desc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #orderby = 'ORDER BY ['+ #currentYear + '] desc'
set #query = 'SELECT name, lost = [1],
draw=[2], won = [3],' + #cols + ', Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select ''dt'', dt union all
select ''result'', case when dt = '+#currentYear+' then result end
) c (old_col_name, new_col)
) x
pivot
(
count(new_col)
for new_col in ([1], [2], [3],' + #cols + ')
) p '+ #orderby
exec sp_executesql #query;
See SQL Fiddle with Demo. This version will give a result:
| NAME | LOST | DRAW | WON | 2014 | 2013 | 2012 | TOTAL |
|----------|------|------|-----|------|------|------|-------|
| Person B | 4 | 0 | 2 | 6 | 2 | 2 | 10 |
| Person A | 2 | 1 | 1 | 4 | 3 | 4 | 11 |
| Person C | 1 | 1 | 1 | 3 | 1 | 0 | 4 |

SQL Server - Complex Dynamic Pivot multiple columns

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

need help in pivoting a data?

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 |

how to convert row to column if unknown number of columns SQL server

I have data like this:
MaGiangVienID | SoTiet1 | SoTiet2 | DateID
79000G07.000206 | 60 | 60.00 | t11
79000G07.000206 | 54 | 54.00 | t12
I want to my result like this:
MaGiangVienID | SoTiet1_t11 | SoTiet2_t11 | SoTiet1_t12 | SoTiet2_t12
79000G07.000206 | 60 | 60.00 | 54 | 54.00
I don't know how many columns because MaGiangVienID have a lot of DateID
Please help me!!Thanks a lot.
For this type of data transformation you need to apply both the UNPIVOT and PIVOT functions. The UNPIVOT takes the data from the multiple columns and places it into rows and then the PIVOT takes the rows and converts it back to columns.
To perform the UNPIVOT all of the values that you convert to rows must be the same datatype so conversion might be needed.
Unpivot:
select MaGiangVienID,
value,
col +'_'+DateId col
from
(
select MaGiangVienID,
cast(SoTiet1 as varchar(50)) SoTiet1,
cast(SoTiet2 as varchar(50)) SoTiet2,
DateID
from yourtable
) src
unpivot
(
value
for col in (SoTiet1, SoTiet2)
) unpiv
See SQL Fiddle with Demo. The result of the unpivot is:
| MAGIANGVIENID | VALUE | COL |
-----------------------------------------
| 79000G07.000206 | 60 | SoTiet1_t11 |
| 79000G07.000206 | 60.00 | SoTiet2_t11 |
| 79000G07.000206 | 54 | SoTiet1_t12 |
| 79000G07.000206 | 54.00 | SoTiet2_t12 |
As you see the UNPIVOT generates the new column names with the DateId appended to the end of it. Now you apply the PIVOT function.
Static PIVOT:
select MaGiangVienID, SoTiet1_t11, SoTiet2_t11, SoTiet1_t12, SoTiet2_t12
from
(
select MaGiangVienID,
value,
col +'_'+DateId col
from
(
select MaGiangVienID,
cast(SoTiet1 as varchar(50)) SoTiet1,
cast(SoTiet2 as varchar(50)) SoTiet2,
DateID
from yourtable
) src
unpivot
(
value
for col in (SoTiet1, SoTiet2)
) unpiv
) src
pivot
(
max(value)
for col in (SoTiet1_t11, SoTiet2_t11, SoTiet1_t12, SoTiet2_t12)
) piv
See SQL Fiddle with Demo
Now the above version works great if you have a known number of DateId values but you stated that you don't so you will want to implement this same query using dynamic sql.
Dynamic PIVOT:
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 not in ('MaGiangVienID', 'DateID')
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT DISTINCT ','
+ quotename(c.name + '_'+t.DateId)
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('MaGiangVienID', 'DateID')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select MaGiangVienID, '+#colsPivot+'
from
(
select MaGiangVienID, value, col +''_''+DateId col
from
(
select MaGiangVienID,
cast(SoTiet1 as varchar(50)) SoTiet1,
cast(SoTiet2 as varchar(50)) SoTiet2,
DateID
from yourtable
) src
unpivot
(
value
for col in ('+#colsUnpivot+')
) unpiv
) src
pivot
(
max(value)
for col in ('+ #colspivot +')
) p'
exec(#query)
See SQL Fiddle with Demo
Both versions produce the same result:
The result is:
| MAGIANGVIENID | SOTIET1_T11 | SOTIET2_T11 | SOTIET1_T12 | SOTIET2_T12 |
---------------------------------------------------------------------------
| 79000G07.000206 | 60 | 60.00 | 54 | 54.00 |
If you do not have access to the UNPIVOT/PIVOT functions then you can replicate the query. The UNPIVOT function can be replicated using a UNION ALL and the PIVOT can be produced using a CASE statement with an aggregate function:
select MaGiangVienID,
max(case when col = 'SoTiet1_t11' then value end) SoTiet1_t11,
max(case when col = 'SoTiet2_t11' then value end) SoTiet2_t11,
max(case when col = 'SoTiet1_t12' then value end) SoTiet1_t12,
max(case when col = 'SoTiet2_t12' then value end) SoTiet2_t12
from
(
select MaGiangVienID, 'SoTiet1_t11' col, cast(SoTiet1 as varchar(50)) value
from yourtable
where DateID = 't11'
union all
select MaGiangVienID, 'SoTiet2_t11' col, cast(SoTiet2 as varchar(50)) value
from yourtable
where DateID = 't11'
union all
select MaGiangVienID, 'SoTiet1_t12' col, cast(SoTiet1 as varchar(50)) value
from yourtable
where DateID = 't12'
union all
select MaGiangVienID, 'SoTiet2_t12' col, cast(SoTiet2 as varchar(50)) value
from yourtable
where DateID = 't12'
) src
group by MaGiangVienID
See SQL Fiddle with Demo
All versions will produce identical results.