How do I dynamically pivot row values into columns without aggregare? - sql

Lets say I have the following table representing results from a user survey system.
SurveyID ResponseID QuestionID Answer
-------- ---------- ---------- ------
1 1 1 'Answer 1'
1 1 2 'Answer 2'
1 1 3 'Answer 3'
1 2 1 'red'
1 2 2 'blue'
1 2 3 'green'
What I want is a pivoted output such that shown below.
SurveyID ResponseID Q1 Q2 Q3
-------- ---------- -- -- --
1 1 'Answer 1' 'Answer 2' 'Answer 3'
1 2 'red' 'blue' 'green'
I know how to achieve this if there were always only the same three questions but this database hosts multiple surveys which could have any number of unique QuestionIDs so I need the Q1, Q2, Q3 columns to be dynamic depending upon the number and IDs of that survey's questions.
I thought this would be a fairly standard problem but I cannot find anything that fully satisfies this issue. Any solution must work with SQL Server 2005.
Hope that makes sense. Thanks.

1) Pivots need an aggregate. You may know in advance that you are only interested in one row, but SQL doesn't know that. If you are only dealing with one row per group, just use MIN() as your aggregate.
2) Dynamic pivot is not a standard problem for SQL. That's a task for the presentation layer, not the data layer. You will have to use dynamic SQL, which still won't be able to handle an arbitrary number of columns and will open up injection attacks if you aren't careful.
If you still want to do it this way:
CREATE TABLE #t (Surveyid int, Responseid int, Questionid int, Answer varchar(max))
INSERT #t VALUES (1,1,1,'Answer1'),(1,1,2,'Answer2'),(1,1,3,'Answer3'),(1,2,1,'red'),(1,2,2,'blue'),(1,2,3,'green')
DECLARE #qids nvarchar(4000)
SELECT #qids = COALESCE(#qids+',','') + qid
FROM (SELECT DISTINCT QUOTENAME(Questionid) qid FROM #t) t
EXEC ('SELECT [SurveyID],[ResponseID],'+#qids+' FROM #t PIVOT(MIN(Answer) FOR Questionid IN('+#qids+')) p')

OK, finally discovered how to do this so thought I would share.
DECLARE #SurveyID SMALLINT;
DECLARE #SQL as VARCHAR(MAX);
DECLARE #Columns AS VARCHAR(MAX);
DECLARE #ColumnHeadings AS Varchar(MAX);
SET #SurveyID = 1;
SELECT
#Columns = COALESCE(#Columns + ', ','') + '[' + QuestionID + ']'
,#ColumnHeadings = COALESCE(#ColumnHeadings + ', ','') + '[' + QuestionID + '] AS [Q' + QuestionNumber + ']'
FROM
(
SELECT DISTINCT
CAST(a.QuestionID AS VARCHAR) AS QuestionID
,CASE WHEN q.QuestionNumber IS NULL THEN '' ELSE q.QuestionNumber END AS QuestionNumber
FROM dbo.Answers AS a
JOIN dbo.Questions AS q
ON a.QuestionID = q.ID
JOIN dbo.SurveyResponses AS r
ON a.ResponseID = r.ID
WHERE r.SurveyID = #SurveyID
)
SET #SQL = '
WITH PivotData AS
(
SELECT
a.QuestionID
,a.ResponseID
,a.Answer
FROM dbo.Answers AS a
JOIN dbo.SurveyResponses AS r
ON a.ResponseID = r.ID
)
SELECT
ResponseID
,' + #ColumnHeadings + '
FROM PivotData
PIVOT
(
MAX(Answer)
FOR QuestionID
IN (' + #Columns + ')
) AS PivotResult
ORDER BY ResponseID' ASC
EXEC (#SQL);

Related

Convert row to column when data are not numbers

I have a Question table, which has a unknown number of questions.(first table in the figure)
I also have an AnswerSheet table, which records student's answer to question.(second table in the figure)
Create table Question
(
Id int,
Text nvarchar(50),
PRIMARY KEY (Id)
)
Create table AnswerSheet
(
StudentId int,
QuestionId int,
Answer nvarchar(50),
PRIMARY KEY (StudentId,QuestionId),
FOREIGN KEY (QuestionId) REFERENCES Question (Id)
)
insert into Question
values(1,'What''s your age'),
(2,'What''s your gender'),
(3,'When do you go home'),
....
insert into AnswerSheet
values(500,1,'20'),
(500,2,'Male'),
(500,3,'5:00pm'),
(501,1,'50'),
(502,2,'I don''t know##'),
....
How do I write a SQL to generate a table like this?
StudentId What's your age What's your gender When do you go home ...
--------- ---------------- ------------------- -------------------
500 20 Male 5:00pm ...
501 50 NULL NULL
502 NULL I don''t know## NULL ...
I feel Pivot is promising but I'm not sure how to use it especially PIVOT requires an aggreation function but my data are not numbers.
Assuming you wanted to go Dynamic
Example
Declare #SQL varchar(max) = Stuff((Select ',' + QuoteName(Text) From Question Order by ID For XML Path('')),1,1,'')
Select #SQL = '
Select *
From (
Select StudentID
,Col = B.Text
,Value = A.Answer
From AnswerSheet A
Join Question B on A.QuestionID=B.ID
) A
Pivot (max(Value) For [Col] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
StudentID What's your age What's your gender When do you go home
500 20 Male 5:00pm
501 50 NULL NULL
502 NULL I don't know## NULL
If it Helps, the Generated SQL Looks Like This
Select *
From (
Select StudentID
,Col = B.Text
,Value = A.Answer
From AnswerSheet A
Join Question B on A.QuestionID=B.ID
) A
Pivot (max(Value) For [Col] in ([What's your age],[What's your gender],[When do you go home]) ) p
I know this question is answered by accepted one, but I hope this approach helps others.
simply you can achieve your goal without using Pivot, via using Group by as next:-
Select b.StudentId,
Min(Case a.text When 'What''s your age' Then b.answer End) 'What''s your age',
Min(Case a.text When 'What''s your gender' Then b.answer End) 'What''s your gender',
Min(Case a.text When 'When do you go home' Then b.answer End) 'When do you go home'
from Question a inner join AnswerSheet b
on a.id = b.Questionid
Group By StudentId
and you mentioned unknown number of questions, so the next code for dynamic:-
DECLARE #DynamicQuestions VARCHAR(8000)
SELECT #DynamicQuestions = Stuff(
(SELECT N' Min(Case a.text When''' + replace (Text,'''','''''')
+ ''' Then b.answer End) '''
+ replace (Text,'''','''''') + ''','
FROM Question FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,1,N'')
select #DynamicQuestions =
left(#DynamicQuestions,len(#DynamicQuestions)-1) -- for Removing last comma
exec ('Select b.StudentId, '+ #DynamicQuestions +
'from Question a inner join AnswerSheet b
on a.id = b.Questionid
Group By StudentId' )
Result:-
StudentId What's your age What's your gender When do you go home
500 20 Male 5:00pm
501 50 NULL NULL
502 NULL I don't know## NULL

Dynamic SELECT statement, generate columns based on present and future values

Currently building a SELECT statement in SQL Server 2008 but would like to make this SELECT statement dynamic, so the columns can be defined based on values in a table. I heard about pivot table and cursors, but seems kind of hard to understand at my current level, here is the code;
DECLARE #date DATE = null
IF #date is null
set # date = GETDATE() as DATE
SELECT
Name,
value1,
value2,
value3,
value4
FROM ref_Table a
FULL OUTER JOIN (
SELECT
PK_ID ID,
sum(case when FK_ContainerType_ID = 1 then 1 else null) Box,
sum(case when FK_ContainerType_ID = 2 then 1 else null) Pallet,
sum(case when FK_ContainerType_ID = 3 then 1 else null) Bag,
sum(case when FK_ContainerType_ID = 4 then 1 else null) Drum
from
Packages
WHERE
#date between PackageStart AND PackageEnd
group by PK_ID ) b on a.Name = b.ID
where
Group = 0
The following works great for me , but PK_Type_ID and the name of the column(PackageNameX,..) are hard coded, I need to be dynamic and it can build itself based on present or futures values in the Package table.
Any help or guidance on the right direction would be greatly appreciated...,
As requested
ref_Table (PK_ID, Name)
1, John
2, Mary
3, Albert
4, Jane
Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
1 , 1, 4, 1JAN2014, 30JAN2014
2 , 2, 3, 1JAN2014, 30JAN2014
3 , 3, 2, 1JAN2014, 30JAN2014
4 , 4, 1, 1JAN2014, 30JAN2014
ContainerType (PK_ID, Type)
1, Box
2, Pallet
3, Bag
4, Drum
and the result should look like this;
Name Box Pallet Bag Drum
---------------------------------------
John 1
Mary 1
Albert 1
Jane 1
The following code like I said works great, the issue is the Container table is going to grow and I need to replicated the same report without hard coding the columns.
What you need to build is called a dynamic pivot. There are plenty of good references on Stack if you search out that term.
Here is a solution to your scenario:
IF OBJECT_ID('tempdb..##ref_Table') IS NOT NULL
DROP TABLE ##ref_Table
IF OBJECT_ID('tempdb..##Packages') IS NOT NULL
DROP TABLE ##Packages
IF OBJECT_ID('tempdb..##ContainerType') IS NOT NULL
DROP TABLE ##ContainerType
SET NOCOUNT ON
CREATE TABLE ##ref_Table (PK_ID INT, NAME NVARCHAR(50))
CREATE TABLE ##Packages (PK_ID INT, FK_ref_Table_ID INT, FK_ContainerType_ID INT, PackageStartDate DATE, PackageEndDate DATE)
CREATE TABLE ##ContainerType (PK_ID INT, [Type] NVARCHAR(50))
INSERT INTO ##ref_Table (PK_ID,NAME)
SELECT 1,'John' UNION
SELECT 2,'Mary' UNION
SELECT 3,'Albert' UNION
SELECT 4,'Jane'
INSERT INTO ##Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
SELECT 1,1,4,'2014-01-01','2014-01-30' UNION
SELECT 2,2,3,'2014-01-01','2014-01-30' UNION
SELECT 3,3,2,'2014-01-01','2014-01-30' UNION
SELECT 4,4,1,'2014-01-01','2014-01-30'
INSERT INTO ##ContainerType (PK_ID, [Type])
SELECT 1,'Box' UNION
SELECT 2,'Pallet' UNION
SELECT 3,'Bag' UNION
SELECT 4,'Drum'
DECLARE #DATE DATE, #PARAMDEF NVARCHAR(MAX), #COLS NVARCHAR(MAX), #SQL NVARCHAR(MAX)
SET #DATE = '2014-01-15'
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(T.[Type])
FROM ##ContainerType T
FOR XML PATH, TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #SQL = 'SELECT [Name], ' + #COLS + '
FROM (SELECT [Name], [Type], 1 AS Value
FROM ##ref_Table R
JOIN ##Packages P ON R.PK_ID = P.FK_ref_Table_ID
JOIN ##ContainerType T ON P.FK_ContainerType_ID = T.PK_ID
WHERE #DATE BETWEEN P.PackageStartDate AND P.PackageEndDate) X
PIVOT (COUNT(Value) FOR [Type] IN (' + #COLS + ')) P
'
PRINT #COLS
PRINT #SQL
SET #PARAMDEF = '#DATE DATE'
EXEC SP_EXECUTESQL #SQL, #PARAMDEF, #DATE=#DATE
Output:
Name Bag Box Drum Pallet
Albert 0 0 0 1
Jane 0 1 0 0
John 0 0 1 0
Mary 1 0 0 0
Static Query:
SELECT [Name],[Box],[Pallet],[Bag],[Drum] FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( [Box],[Pallet],[Bag],[Drum])
) AS PivotTable
) AS Main
ORDER BY RFID
Dynamic Query:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + [Type] + ']'
FROM ContanerType
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT [Name],' + #columnList + ' FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( ' + #columnList + ')
) AS PivotTable
) AS Main
ORDER BY RFID;'
EXEC sp_executesql #pivotsql
Following my tutorial below will help you to understand the PIVOT functionality:
We write sql queries in order to get different result sets like full, partial, calculated, grouped, sorted etc from the database tables. However sometimes we have requirements that we have to rotate our tables. Sounds confusing?
Let's keep it simple and consider the following two screen grabs.
SQL Table:
Expected Results:
Wow, that's look like a lot of work! That is a combination of tricky sql, temporary tables, loops, aggregation......, blah blah blah
Don't worry let's keep it simple, stupid(KISS).
MS SQL Server 2005 and above has a function called PIVOT. It s very simple to use and powerful. With the help of this function we will be able to rotate sql tables and result sets.
Simple steps to make it happen:
Identify all the columns those will be part of the desired result set.
Find the column on which we will apply aggregation(sum,ave,max,min etc)
Identify the column which values will be the column header.
Specify the column values mentioned in step3 with comma separated and surrounded by square brackets.
So, if we now follow above four steps and extract information from the above sales table, it will be as below:
Year, Month, SalesAmount
SalesAmount
Month
[Jan],[Feb] ,[Mar] .... etc
We are nearly there if all the above steps made sense to you so far.
Now we have all the information we need. All we have to do now is to fill the below template with required information.
Template:
Our SQL query should look like below:
SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( [Jan],[Feb] ,[Mar],
[Apr],[May],[Jun] ,[Jul],
[Aug],[Sep] ,[Oct],[Nov] ,[Dec])
) AS PivotTable;
In the above query we have hard coded the column names. Well it's not fun when you have to specify a number of columns.
However, there is a work arround as follows:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + SalesMonth + ']'
FROM Sales
GROUP BY SalesMonth
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( ' + #columnList +' )
) AS PivotTable;'
EXEC sp_executesql #pivotsql
Hopefully this tutorial will be a help to someone somewhere.
Enjoy coding.

Querying a junction/link/bridge table, column data as header

I have a list of specialties (upwards of 20)
SpecialtyID Description
------------------------
1 Specialty1
2 Specialty2
3 Specialty3
I have a list of providers (upwards of 50)
ProviderID Name
------------------------
1 Tom
2 Maria
3 Pat
Each provider can have multiple specialties, each specialty can have multiple providers - so a many to many relationship.
I have a junction/link/bridge table called SpecialtyProvider and if I simply query the link table with the following query, I get the table below.
SELECT SpecialtyID, ProviderID FROM SpecialtyProvider
SpecialtyID ProviderID
------------------------
1 1
2 1
3 1
1 2
2 2
3 3
What I would like to do, is pull out the data formatted like so:
SpecialtyID ProviderID=1 ProviderID=2 ProviderID=3 ProviderID=x
-----------------------------------------------------------
1 true true NULL
2 true true NULL
3 true NULL true
Once I can format the data correctly, I'll be dumping this into an ASP ListView.
I am not quite sure how to proceed. I have read 100 posts about different variations of the PIVOT command, but where I don't have an aggregate function, I haven't been able to make any of the other examples/solutions/groupings make sense.
If you need to pivot without using an aggregate, you can usually just use MAX (you're essentially taking the MAX of a single value, which is just that same value).
select SpecialtyID, case when [1] is not null then 'true' end 'ProviderID=1',
case when [2] is not null then 'true' end 'ProviderID=2',
case when [3] is not null then 'true' end 'ProviderID=3'
from (
select s.SpecialtyID, s.Description, sp.ProviderID
from Specialty s
join SpecialtyProvider sp on sp.SpecialtyID = s.SpecialtyID
) x
pivot(
MAX(Description)
for ProviderID in ([1],[2],[3])
) pvt
SQL Fiddle
However, it's also possible to get the same results without using PIVOT at all:
select s.SpecialtyID,
Max(case when sp.ProviderID = 1 then 'true' end) 'ProviderID=1',
Max(case when sp.ProviderID = 2 then 'true' end) 'ProviderID=2',
Max(case when sp.ProviderID = 3 then 'true' end) 'ProviderID=3'
from Specialty s
join SpecialtyProvider sp on sp.SpecialtyID = s.SpecialtyID
group by s.SpecialtyID
I find this easier to read, and it will probably be faster as well.
SQL Fiddle
With all that said, you may want to reconsider your UI. Having a table 50 columns wide will be difficult for a user to process. It might make sense to filter the data so the user can only view specific portions of it. Also, if you're dealing with a variable number of providers, it may make sense to pull all the data up to the web server and process it in your ASP codebehind.
The following blog post introduces the concept of a dynamic pivot where you do not have to specify you columns so as to address the X factor for Providers. http://beyondrelational.com/modules/2/blogs/70/posts/10840/dynamic-pivot-in-sql-server-2005.aspx
I took it a bit further and also print out the generated SQL. Here is what I came up for to address your example above.
IF (OBJECT_ID(N'dynamic_pivot', N'P') IS NOT NULL)
DROP PROCEDURE dynamic_pivot
GO
CREATE PROCEDURE dynamic_pivot
(
#select VARCHAR(2000)
, #PivotCol VARCHAR(100)
, #Summaries VARCHAR(100)
, #GenerateScript BIT = 1
)
AS
BEGIN
SET NOCOUNT ON ;
DECLARE #pivot VARCHAR(MAX)
, #sql VARCHAR(MAX)
SELECT #select = REPLACE(#select, 'SELECT ',
'SELECT ' + #PivotCol + ' AS pivot_col, ')
CREATE TABLE #pivot_columns
(
pivot_column VARCHAR(100)
)
SELECT #sql = 'SELECT DISTINCT pivot_col FROM (' + #select + ') AS t'
INSERT INTO #pivot_columns
EXEC ( #sql
)
SELECT #pivot = COALESCE(#pivot + ',', '') + '[' + pivot_column + ']'
FROM #pivot_columns
SELECT #sql = '
SELECT *
FROM
(
' + #select + '
) AS t
PIVOT
(
' + #Summaries + ' for pivot_col in (' + #pivot + ')
) AS p'
PRINT #sql
EXEC(#sql)
END
GO
EXEC [dbo].[dynamic_pivot] #select = 'SELECT SpecialtyID, 1 AS hasSpecialty FROM SpecialtyProvider', -- varchar(2000)
#PivotCol = 'ProviderID', -- varchar(100)
#Summaries = 'COUNT(hasSpecialty)' -- varchar(100)
The resulting query that is displayed in your message window in SSMS is the following:
SELECT *
FROM
(
SELECT ProviderID AS pivot_col, SpecialtyID, 1 AS hasSpecialty FROM SpecialtyProvider
) AS t
PIVOT
(
COUNT(hasSpecialty) for pivot_col in ([1],[2],[3])
) AS p
You can modify this to give you the column names and values that are required.

Not able to combine multiple rows into single row based on certain conditions

In the image above, i have shown table structure i use to store result of student. However I need to select data in such a manner such that depending on particular FEID(examination ID),
I get marks obtained and subID of single student in single row. Something like below:
FEID SubID1 MarksObtained SubID2 MarksObtained SubID3 MarksObtained StdID
2 1 0 2 0 3 0 50
2 1 45 2 45 3 45 51
Result Column wont affect outcome as for a particular stdID and FEID it remains same for no matter how many SubID are there.
Basically I am storing each subject marks in single row and subjects are can be any number( more than 3 as in this case) , which is not known before hand. But for each I create one row to enter its marks
I tried sytax below .
DECLARE #cols nvarchar(MAX);
--get the list of subids from the table
SELECT #cols = SubjectName from tbSubjects where SubID IN(select distinct SubID from tbFinalMarks);
Declare #sql nvarchar(MAX) = 'SELECT StdId, FEID, ' + #cols + 'FROM
(
SELECT * FROM tbFinalMarks
)t
PIVOT
(
MAX(MarksObtained) FOR SubId IN (' + #cols + ')
)p';
Something like this will do it. It will also dynamically add new columns for new sub ids without you needing to worry about it.
DECLARE #cols nvarchar(MAX);
--get the list of subids from the table
SELECT #cols = COALESCE(#cols + ',', '') + '[' + CAST(SubId AS nvarchar) + ']' FROM (SELECT DISTINCT SubId FROM table);
Declare #sql nvarchar(MAX) = 'SELECT StdId, FEID, ' + #cols + 'FROM
(
SELECT * FROM table
)t
PIVOT
(
MAX(MarksObtained) FOR SubId IN (' + #cols + ')
)p';
EXECUTE sp_executesql #sql;
Although you can use pivot, I think the explicit aggregation approach is easier to construct:
select feid,
1 as SubId_1,
max(case when SubId = 1 then MarksObtained end) as MarksObtained_1,
2 as SubId_2,
max(case when SubId = 2 then MarksObtained end) as MarksObtained_2,
3 as SubId_3,
max(case when SubId = 3 then MarksObtained end) as MarksObtained_3,
stdid
from table t
group by feid, stdid;

Make 1 row out of many rows from another table

I have a MSSQL database which in one table holds bio info about a person:
ID: Name : Age : Sex
In another table it holds their answers to a number of questions like this:
PersonID : QuestionID : Answer
Is it possible to display all of them via MSSQLMSE into one record like this:
ID : Name : Age : Sex : Question1Answer : Question2Answer : Question3Answer : And so on?
Try this:
SELECT ID, name, age, sex, Question1answer, Question2answer
FROM
(
SELECT
p.Id,
p.Name,
p.Age,
p.sex,
questionanswer = 'Question' + CAST(q.questionid AS VARCHAR(10)) + 'answer',
q.Answer
FROM Persons p
INNER JOIN Questions q ON p.Id = q.UserID
) t
PIVOT
(
MAX(Answer)
FOR questionanswer IN([Question1answer], [Question2answer])
) p;
SQL Fiddle Demo
This will give you:
| ID | NAME | AGE | SEX | QUESTION1ANSWER | QUESTION2ANSWER |
-----------------------------------------------------------------
| 1 | Ahmed | 25 | M | Yes | No |
| 2 | Mohammed | 30 | M | No | Never |
| 3 | Sara | 25 | F | No | Never |
However: If you want to do this dynamically for any number of questions per user, you can do this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct
',' +
QUOTENAME('Question' +
CAST(questionid AS VARCHAR(10)) +
'answer')
FROM questions
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #query = 'SELECT ID, name, age, sex, ' + #cols +
' FROM
(
SELECT
p.Id,
p.Name,
p.Age,
p.sex,
questionanswer = ''Question'' +
CAST(q.questionid AS VARCHAR(10)) +
''answer'',
q.Answer
FROM Persons p
INNER JOIN Questions q ON p.Id = q.UserID
) t
PIVOT
(
MAX(Answer)
FOR questionanswer IN( ' + #cols + ') ) p ';
SQL Fiddle Dynamic Demo
select ID,Name,Age,Sex,
(select top 1 Answer from Questions
where PersonId=prerson.id AND QuestionID=1) as Question1Answer,
(select top 1 Answer from Questions
where PersonId=prerson.id AND QuestionID=2) as Question2Answer,
(select top 1 Answer from Questions
where PersonId=prerson.id AND QuestionID=3) as Question3Answer
from prerson
The PIVOT command in sql server would be appropriate to transpose table results, see http://msdn.microsoft.com/en-us/library/ms177410(v=sql.100).aspx
The script below works with a predefined set of questions. For a variable amount of columns this link would help http://blog-mstechnology.blogspot.com/2010/06/t-sql-pivot-operator-with-dynamic.html.
DECLARE #Person TABLE(
ID INT,
Name VARCHAR(50),
Age INT,
Sex CHAR(1)
)
DECLARE #Questions TABLE(
ID INT,
QuestionID INT,
Question VARCHAR(200),
Answer VARCHAR(200)
)
INSERT INTO #Person VALUES
(1,'Andrew',56,'M'),
(2,'Marge',65,'F')
INSERT INTO #Questions VALUES
(1,1,'Question1','Andrews Answer 1'),
(1,2,'Question2','Andrews Answer 2'),
(2,1,'Question1','Marge Answer 1'),
(2,3,'Question3','Marge Answer 3')
SELECT ID,Age,Name,Sex,
[1] AS 'Question1Answer',
[2] AS 'Question2Answer',
[3] AS 'Question3Answer'
FROM(
SELECT P.ID,P.Age,P.Name,P.Sex,Q.QuestionID,Q.Answer
FROM #Person P
INNER JOIN #Questions Q ON Q.ID = P.ID
) Source
PIVOT
(
MAX(Answer)
FOR QuestionID IN ([1],[2],[3])
)AS PT
Try this:
SELECT
ID,
Name,
Age,
Sex,
[1] AS Question1Answer,
[2] AS Question1Answer,
[3] AS Question1Answer
FROM
(SELECT
i.ID,
i.Name,
i.Age,
i.Sex,
q.QuestionId,
q.Answer
FROM
dbo.Info AS i
INNER JOIN dbo.Questions AS q on q.ID = i.ID) AS m
PIVOT
(
MAX(Answer) FOR QuestionId in ([1],[2],[3])
) as PivotTable
I think this (SQL Fiddle here) is what you are looking for. For a given person id;
Note: p = Person Table, a = Answer Table and qId = QuestionId
DECLARE #S VARCHAR(Max)
DECLARE #PersonId INT = 1
SELECT #S=ISNULL(#S+' : ','') + convert(varchar(10), id) +' : '+ name
+' : '+ convert(varchar(10), age)+' : '+sex
FROM p
WHERE p.Id = #PersonId
SELECT #S=ISNULL(#S+' : ','') +
'Question' + convert(varchar(10), qid) +'Answer : '+ answer
FROM a
WHERE pId = #PersonId
SELECT #S
--Following results is in one line
--1 : X : 25 : M : Question11Answer : Here is my answer for 11 :
--Question12Answer : Here is my answer for 12 :
--Question101Answer : Here is my answer for 101
Try this method: (Set compatability level of database to 90 or higher)
begin
Declare #MainSQL nVarchar(Max)
Declare #Param nVarchar(Max)
Declare #QId int
set #Param = ''
Declare curParam Cursor For select Distinct QuestionID from Questions
open curParam
Fetch next from curParam into #QId
While ##Fetch_Status = 0
Begin
set #Param = #Param +'[' +convert(nVarchar,#QId ) + '],'
Fetch next from curParam into #QId
End
close curParam;
Deallocate curParam;
set #Param = left(#Param,len(#Param)-1)
Set #MainSQL = 'Select ID,Name,Age,***
From
(select P.ID,Name,Age,
Q.QuestionID, Q.Answer From Person P Left outer join
Questions Q
ON P.ID = Q.PersonID) S
Pivot
(Max(Answer)
For QuestionID in(***)
) as Pvt'
Set #MainSQL =replace(#MainSQL,'***',#Param);
--print #MainSQL
execute sp_executesql #MainSQL
End