Increment value in SQL SELECT statement - sql

Here is the sample code for what I am trying to do and below is the result:
CREATE TABLE dbo.#TempDoc_DocContRoles (DocID int null, FullName varchar(500), DocContRole
varchar (100), NumRole int null)
INSERT INTO #TempDoc_DocContRoles(DocID, FullName, DocContRole)
SELECT
d.DocID, c.FirstName + ' ' + c.LastName as FullName, ldcro.DocContRole
FROM
Document as d
JOIN
dbo.Split( ',','30,31') AS l ON d.DocID = cast(l.[Value] AS int)
JOIN
Doc_Contact AS dc ON d.DocID = dc.DocID
JOIN
Contact AS c ON dc.P_Number = c.P_Number
LEFT JOIN
lkpDocContactRole AS ldcro ON ldcro.DocContRoleID = dc.DocContRoleID
JOIN
dbo.Split( ',','1,2,7') AS r ON ldcro.DocContRoleID = cast(r.[Value] AS int)
CREATE TABLE dbo.#MaxNumRoles (DocID int null, DocContRole varchar(100), NumRole int null)
INSERT INTO dbo.#MaxNumRoles (DocID,DocContRole,NumRole)
SELECT
DocID, DocContRole, COUNT(*)
FROM
dbo.#TempDoc_DocContRoles
GROUP BY
DocID, DocContRole
HAVING
Count(*) > 0
UPDATE td
SET td.NumRole = mr.NumRole
FROM dbo.#TempDoc_DocContRoles as td
INNER JOIN dbo.#MaxNumRoles as mr ON td.DocContRole = mr.docContRole
SELECT * FROM dbo.#TempDoc_DocContRoles
DROP TABLE dbo.#TempDoc_DocContRoles
DROP TABLE dbo.#MaxNumRoles
Result:
DocID FullName DocContRole NumRole
30 Smith Author 3
30 Daln Staff 2
30 Dolby Author 3
31 Tammy Author 3
30 Barny Author 3
30 Sanny Res Coor 1
30 Johny Staff Rev 2
I would like to actually get:
DocID FullName DocContRole NumRole
30 Smith Author 1
30 Daln Staff 1
30 Dolby Author 2
31 Tammy Author 1
30 Barny Author 3
30 Sanny Res Coor 1
30 Johny Staff Rev 2
It should increment the number in NumRole per docContRole and docID (ex Author 1, Author 2 etc). Currently it gives the total number of authors per DocID.
My ultimate goal is to get something like
DocID Author_1 Author_2 Author_3 Staff_1 Staff_2 ResCoor_1
30 Smith Dolby Barny Daln Johny Sanny
31 Tammy

You can try this
select
td.DocID, td.FullName, td.DocContRole,
row_number() over (partition by td.DocID, td.DocContRole order by td.FullName) as NumRole
from dbo.#TempDoc_DocContRoles as td
So dynamic SQL will be smth like that
SQL FIDDLE EXAMPLE
create table #t2
(
DocID int, FullName nvarchar(max),
NumRole nvarchar(max)
)
declare #pivot_columns nvarchar(max), #stmt nvarchar(max)
insert into #t2
select
td.DocID, td.FullName,
td.DocContRole +
cast(
row_number() over
(partition by td.DocID, td.DocContRole order by td.FullName)
as nvarchar(max)) as NumRole
from t as td
select
#pivot_columns =
isnull(#pivot_columns + ', ', '') +
'[' + NumRole + ']'
from (select distinct NumRole from #t2) as T
select #stmt = '
select *
from #t2 as t
pivot
(
min(FullName)
for NumRole in (' + #pivot_columns + ')
) as PT'
exec sp_executesql
#stmt = #stmt

Related

Embed child records into single row with parent information

I have the following tables
BATCH
BatchID Name CustomerID DateCreated Status
12 A 1 01/01/2013 Active
13 B 12 01/01/2013 Inactive
14 C 245 01/01/2013 Complete
BATCHDETAIL
BatchDetailID BatchID Weight Price DestinationCode
1 12 55 500.00 99
2 12 119 1500.00 55
3 13 12 133 1212
A batch record can have many batch detail records linked via the FK BatchDetail.BatchID
I want to write a query to select a single row back to the user which combines the information in the BATCH record and the Weight,Price and DestinationCode from both BATCHDETAIL records for BatchID = 12
So the output would be :
BatchID Name CustomerID DateCreated Status WeightA PriceA DestinationCodeA WeightB PriceB DestinationCodeB
12 A 1 01/01/2013 Active 55 500.00 99 119 1500 55
So you can see I want to have 1 row with all information combined in the one row and differentiate each detail record with A or B ( Lets assume a maximum of 2 detail records is only allowed )
I have thought of creating a table with these fields and then building up the information in a series of select statements and finally doing a select on the temp table but getting the query into a single block of SQL would be ideal.
Here is a solution using dynamic SQL:
-- Get the MAX total number of records per BatchID (how many sets of columns do we need?)
DECLARE #requiredLevels int = (SELECT MAX(C) FROM (SELECT COUNT(*) C FROM BATCHDETAIL GROUP BY BatchID) Q)
;
-- Build a dynamic statement for the final SELECT fields
DECLARE
#finalFieldsSQL varchar(1000) = ''
, #finalFieldsN int = 1
;
WHILE #finalFieldsN <= #requiredLevels
BEGIN
SET #finalFieldsSQL = #finalFieldsSQL + ', Weight' + CHAR(64 + #finalFieldsN) + ', Price' + CHAR(64 + #finalFieldsN) + ', DestinationCode' + CHAR(64 + #finalFieldsN)
SET #finalFieldsN = #finalFieldsN + 1
END
-- Build a dynamic statement for the subquery SELECT fields
DECLARE
#subqueryFieldsSQL varchar(1000) = ''
, #subqueryFieldsN int = 1
;
WHILE #subqueryFieldsN <= #requiredLevels
BEGIN
SET #subqueryFieldsSQL = #subqueryFieldsSQL + ', MAX([' + CAST(#subqueryFieldsN AS varchar) + ']) ColumnName' + CHAR(64 + #subqueryFieldsN)
SET #subqueryFieldsN = #subqueryFieldsN + 1
END
-- Build a dynamic statement for the PIVOT fields
DECLARE
#pivotFieldsSQL varchar(1000) = ''
, #pivotFieldsN int = 1
;
WHILE #pivotFieldsN <= #requiredLevels
BEGIN
SET #pivotFieldsSQL = #pivotFieldsSQL + ', [' + CAST(#pivotFieldsN AS varchar) + ']'
SET #pivotFieldsN = #pivotFieldsN + 1
END
SET #pivotFieldsSQL = SUBSTRING(#pivotFieldsSQL, 3, LEN(#pivotFieldsSQL) - 2)
-- Build the final SQL statement and execute
DECLARE #SQL varchar(8000) =
'
SELECT
B.BatchID, B.Name, B.CustomerID, B.DateCreated, [Status]' + #finalFieldsSQL + '
FROM
BATCH B
LEFT JOIN
(
SELECT
BatchID' + REPLACE(#subqueryFieldsSQL, 'ColumnName', 'Weight') + '
FROM
(
SELECT BD.BatchID, [Weight], ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
FROM
BATCH B
JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
) Q
PIVOT
(
MAX([Weight])
FOR R IN (' + #pivotFieldsSQL + ')
) P
GROUP BY BatchID
) W
ON B.BatchID = W.BatchID
LEFT JOIN
(
SELECT
BatchID' + REPLACE(#subqueryFieldsSQL, 'ColumnName', 'Price') + '
FROM
(
SELECT BD.BatchID, Price, ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
FROM
BATCH B
JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
) Q
PIVOT
(
MAX(Price)
FOR R IN (' + #pivotFieldsSQL + ')
) P
GROUP BY BatchID
) P
ON B.BatchID = P.BatchID
LEFT JOIN
(
SELECT
BatchID' + REPLACE(#subqueryFieldsSQL, 'ColumnName', 'DestinationCode') + '
FROM
(
SELECT BD.BatchID, DestinationCode, ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
FROM
BATCH B
JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
) Q
PIVOT
(
MAX(DestinationCode)
FOR R IN (' + #pivotFieldsSQL + ')
) P
GROUP BY BatchID
) D
ON B.BatchID = D.BatchID
'
EXEC (#SQL)
If you don't want to show empty records, replace LEFT JOIN with JOIN in the final statement (3 occurences).
you can use Pivot and UnPivot to achieve this result. Try Something like this:
SELECT
BatchID,[Name],[CustomerID],[DateCreated],[Status],
MAX(Weight1) as WeightA,
MAX(Price1) as PriceA,
MAX(DestinationCode1) as DestinationCodeA,
MAX(Weight2) as WeightB,
MAX(Price2) as PriceB,
MAX(DestinationCode2) as DestinationCodeB
FROM (
SELECT *,COL + CAST(DENSE_RANK() OVER (PARTITION BY Batchid ORDER BY BatchDetailID ASC) AS VARCHAR) AS BATCHPIVOT
FROM
(
SELECT b.*,cast(d.Weight as varchar(255)) as Weight, cast(d.Price as varchar(255)) as Price, cast(d.DestinationCode as varchar(255)) as DestinationCode,d.BatchDetailID
FROM #Batch B
INNER JOIN #BATCHDETAIL D on b.BatchID = d.BatchID
) AS cp
UNPIVOT
(
Val FOR Col IN ([Weight], [Price], [DestinationCode])
) AS up
) AS query
PIVOT (MAX(Val)
FOR BATCHPIVOT IN (Weight1,Price1,DestinationCode1, Weight2, Price2, DestinationCode2)) AS Pivot1
GROUP BY BatchID,[Name],[CustomerID],[DateCreated],[Status]
This is just standard query. you can make this script Dynamic according to your liking:
Complete Script:
Create table #Batch
(BatchID int,
[Name] char(1),
[CustomerID] int,
[DateCreated] date,
[Status] varchar(50)
)
Create table #BATCHDETAIL
(BatchDetailID int,
BatchID int,
[Weight] int,
Price money,
DestinationCode int
)
INSERT INTO #Batch
VALUES(12,'A',1,'01/01/2013','Active')
,(13,'B',12,'01/01/2013','Inactive')
,(14,'C',245,'01/01/2013','Complete')
INSERT INTO #BATCHDETAIL
VALUES(1,12,55,500.00,99)
,(2,12,119,1500.00,55)
,(3,13,12,133,1212)
SELECT
BatchID,[Name],[CustomerID],[DateCreated],[Status],
MAX(Weight1) as WeightA,
MAX(Price1) as PriceA,
MAX(DestinationCode1) as DestinationCodeA,
MAX(Weight2) as WeightB,
MAX(Price2) as PriceB,
MAX(DestinationCode2) as DestinationCodeB
FROM (
SELECT *,COL + CAST(DENSE_RANK() OVER (PARTITION BY Batchid ORDER BY BatchDetailID ASC) AS VARCHAR) AS BATCHPIVOT
FROM
(
SELECT b.*,cast(d.Weight as varchar(255)) as Weight, cast(d.Price as varchar(255)) as Price, cast(d.DestinationCode as varchar(255)) as DestinationCode,d.BatchDetailID
FROM #Batch B
INNER JOIN #BATCHDETAIL D on b.BatchID = d.BatchID
) AS cp
UNPIVOT
(
Val FOR Col IN ([Weight], [Price], [DestinationCode])
) AS up
) AS query
PIVOT (MAX(Val)
FOR BATCHPIVOT IN (Weight1,Price1,DestinationCode1, Weight2, Price2, DestinationCode2)) AS Pivot1
GROUP BY BatchID,[Name],[CustomerID],[DateCreated],[Status]

Stored procedure that returns a table from 2 combined

I am trying to write a stored procedure which returns a result combining 2 table variables which looks something like this.
Name | LastName | course | course | course | course <- Columns
Name | LastName | DVA123 | DVA222 | nothing | nothing <- Row1
Pete Steven 200 <- Row2
Steve Lastname 50 <- Row3
From these 3 tables
Table Staff:
Name | LastName | SSN |
Steve Lastname 234
Pete Steven 132
Table Course Instance:
Course | Year | Period |
DVA123 2013 1
DVA222 2014 2
Table Attended by:
Course | SSN | Year | Period | Hours |
DVA123 234 2013 1 200
DVA222 132 2014 2 50
I am taking #year as a parameter that will decide what year in the course will be displayed in the result.
ALTER proc [dbo].[test4]
#year int
as
begin
-- I then declare the 2 tables which I will then store the values from the tables
DECLARE #Table1 TABLE(
Firstname varchar(30) NOT NULL,
Lastname varchar(30) NOT NULL
);
DECLARE #Table2 TABLE(
Course varchar(30) NULL
);
Declare #variable varchar(max) -- variable for saving the cursor value and then set the course1 to 4
I want at highest 4 results/course instances which I later order by the period of the year
declare myCursor1 CURSOR
for SELECT top 4 period from Course instance
where year = #year
open myCursor1
fetch next from myCursor1 into #variable
--print #variable
while ##fetch_status = 0
Begin
UPDATE #Table2
SET InstanceCourse1 = #variable
where current of myCursor1
fetch next from myCursor1 into #variable
print #variable
End
Close myCursor1
deallocate myCursor1
insert into #table1
select 'Firstname', 'Lastname'
insert into #table1
select Firstname, Lastname from staff order by Lastname
END
select * from #Table1 -- for testing purposes
select * from #Table2 -- for testing purposes
--Then i want to combine these tables into the output at the top
This is how far I've gotten, I don't know how to get the courses into the columns and then get the amount of hours for each staff member.
If anyone can help guide me in the right direction I would be very grateful. My idea about the cursor was to get the top (0-4) values from the top4 course periods during that year and then add them to the #table2.
Ok. This is not pretty. It is a really ugly dynamic sql, but in my testing it seems to be working. I have created an extra subquery to get the courses values as the first row and then Union with the rest of the result. The top four courses are gathered by using ROW_Number() and order by Year and period. I had to make different versions of the courses string I am creating in order to use them for both column names, and in my pivot. Give it a try. Hopefully it will work on your data as well.
DECLARE #Year INT
SET #Year = 2014
DECLARE #Query NVARCHAR(2000)
DECLARE #CoursesColumns NVARCHAR(2000)
SET #CoursesColumns = (SELECT '''' + Course + ''' as c' + CAST(ROW_NUMBER() OVER(ORDER BY Year, Period) AS nvarchar(50)) + ',' AS 'data()'
FROM AttendedBy where [Year] = #Year
for xml path(''))
SET #CoursesColumns = LEFT(#CoursesColumns, LEN(#CoursesColumns) -1)
SET #CoursesColumns =
CASE
WHEN CHARINDEX('c1', #CoursesColumns) = 0 THEN #CoursesColumns + 'NULL as c1, NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c2', #CoursesColumns) = 0 THEN #CoursesColumns + ',NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c3', #CoursesColumns) = 0 THEN #CoursesColumns + ', NULL as c3, NULL as c4'
WHEN CHARINDEX('c4', #CoursesColumns) = 0 THEN #CoursesColumns + ', NULL as c4'
ELSE #CoursesColumns
END
DECLARE #Courses NVARCHAR(2000)
SET #Courses = (SELECT Course + ' as c' + CAST(ROW_NUMBER() OVER(ORDER BY Year, Period) AS nvarchar(50)) + ',' AS 'data()'
FROM AttendedBy where [Year] = #Year
for xml path(''))
SET #Courses = LEFT(#Courses, LEN(#Courses) -1)
SET #Courses =
CASE
WHEN CHARINDEX('c1', #Courses) = 0 THEN #Courses + 'NULL as c1, NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c2', #Courses) = 0 THEN #Courses + ',NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c3', #Courses) = 0 THEN #Courses + ', NULL as c3, NULL as c4'
WHEN CHARINDEX('c4', #Courses) = 0 THEN #Courses + ', NULL as c4'
ELSE #Courses
END
DECLARE #CoursePivot NVARCHAR(2000)
SET #CoursePivot = (SELECT Course + ',' AS 'data()'
FROM AttendedBy where [Year] = #Year
for xml path(''))
SET #CoursePivot = LEFT(#CoursePivot, LEN(#CoursePivot) -1)
SET #Query = 'SELECT Name, LastName, c1, c2, c3, c4
FROM (
SELECT ''Name'' as name, ''LastName'' as lastname, ' + #CoursesColumns +
' UNION
SELECT Name, LastName,' + #Courses +
' FROM(
SELECT
s.Name
,s.LastName
,ci.Course
,ci.Year
,ci.Period
,CAST(ab.Hours AS NVARCHAR(100)) AS Hours
FROM Staff s
LEFT JOIN AttendedBy ab
ON
s.SSN = ab.SSN
LEFT JOIN CourseInstance ci
ON
ab.Course = ci.Course
WHERE ci.Year=' + CAST(#Year AS nvarchar(4)) +
' ) q
PIVOT(
MAX(Hours)
FOR
Course
IN (' + #CoursePivot + ')
)q2
)q3'
SELECT #Query
execute(#Query)
Edit: Added some where clauses so only courses from given year is shown. Added Screenshot of my results.
try this
DECLARE #CourseNameString varchar(max),
#query AS NVARCHAR(MAX);
SET #CourseNameString=''
select #CourseNameString = STUFF((SELECT distinct ',' + QUOTENAME(Course)
FROM Attended where [Year]= 2013
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = '
select Name,LastName,'+#CourseNameString+' from Staff as e inner join (
SELECT * FROM
(SELECT [Hours],a.SSN,a.Course as c FROM Attended as a inner JOIN Staff as s
ON s.SSN = s.SSN) p
PIVOT(max([Hours])FOR c IN ('+#CourseNameString+')) pvt)p
ON e.SSN = p.SSN'
execute(#query)
Use subquery like this one :
SELECT Firstname, Lastname, (select instanceCourse1 from table2) as InstanceCourse1 from Table1

SQL Server : how to join two tables and rotate the second table horizontally

I have two tables, I need to present in one table a list of staff and horizontally all the clients of the same company there can be up to 50 clients
table one:
Company_id staff name email
1 John john#abc
1 Mandy mandy#aaa
2 Jane Jane#jlkj
3 Andy Andy#uuu
Table two:
Company_id client name client class
1 a 02
2 ss 01
2 d 08
The Result table:
Company_Id staff name email client 1 client 2 client3 ….. clientX
1 John john#abc a(02)
1 Mandy mandy#aaa a(02)
2 Jane Jane#jlkj ss(01) d(08)
Try this one -
CREATE TABLE dbo.staff
(
company_id INT
, staff_name NVARCHAR(50)
, email NVARCHAR(50)
)
INSERT INTO dbo.staff (company_id, staff_name, email)
VALUES
(1, 'John', 'john#abc'),
(1, 'Mandy', 'mandy#aaa'),
(2, 'Jane', 'Jane#jlkj'),
(3, 'Andy', 'Andy#uuu')
CREATE TABLE dbo.client
(
company_id INT
, client_id INT
, client_name NVARCHAR(50)
, client_class NVARCHAR(50)
)
INSERT INTO dbo.client (company_id, client_id, client_name, client_class)
VALUES
(1, 1, 'a', '02'),
(2, 2, 'ss', '01'),
(2, 3, 'd', '08')
DECLARE
#Cols NVARCHAR(MAX)
, #SQL NVARCHAR(MAX)
SELECT #Cols =
STUFF((
SELECT DISTINCT ',' + QUOTENAME(c.client_id)
FROM dbo.client c
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
, 1 , 1, '')
SELECT #SQL = '
SELECT s.staff_name, s.email, d.*
FROM (
SELECT
company_id
, ' + #Cols + '
FROM (
SELECT name = client_name + ''('' + client_class + '')'', client_id, company_id
FROM dbo.client
) x
PIVOT
(
MAX(name)
FOR client_id IN (' + #Cols + ')
) p
) d
JOIN dbo.staff s ON s.company_id = d.company_id
'
EXEC sys.sp_executesql #SQL
SELECT s.company_id
, s.staff_name
, s.email
, c.clients
FROM dbo.staff s
OUTER APPLY (
SELECT clients = STUFF((
SELECT N', ' + c.client_name + '(' + c.client_class + ')'
FROM dbo.client c
WHERE c.company_id = s.company_id
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)'), 1, 2, '')
) c
WHERE c.clients IS NOT NULL
USE FOR XML CLAUSE and SUBQUERY
Solution

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

Grouping runs of data

SQL Experts,
Is there an efficient way to group runs of data together using SQL?
Or is it going to be more efficient to process the data in code.
For example if I have the following data:
ID|Name
01|Harry Johns
02|Adam Taylor
03|John Smith
04|John Smith
05|Bill Manning
06|John Smith
I need to display this:
Harry Johns
Adam Taylor
John Smith (2)
Bill Manning
John Smith
#Matt: Sorry I had trouble formatting the data using an embedded html table it worked in the preview but not in the final display.
Try this:
select n.name,
(select count(*)
from myTable n1
where n1.name = n.name and n1.id >= n.id and (n1.id <=
(
select isnull(min(nn.id), (select max(id) + 1 from myTable))
from myTable nn
where nn.id > n.id and nn.name <> n.name
)
))
from myTable n
where not exists (
select 1
from myTable n3
where n3.name = n.name and n3.id < n.id and n3.id > (
select isnull(max(n4.id), (select min(id) - 1 from myTable))
from myTable n4
where n4.id < n.id and n4.name <> n.name
)
)
I think that'll do what you want. Bit of a kludge though.
Phew! After a few edits I think I have all the edge cases sorted out.
I hate cursors with a passion... but here's a dodgy cursor version...
Declare #NewName Varchar(50)
Declare #OldName Varchar(50)
Declare #CountNum int
Set #CountNum = 0
DECLARE nameCursor CURSOR FOR
SELECT Name
FROM NameTest
OPEN nameCursor
FETCH NEXT FROM nameCursor INTO #NewName
WHILE ##FETCH_STATUS = 0
BEGIN
if #OldName <> #NewName
BEGIN
Print #OldName + ' (' + Cast(#CountNum as Varchar(50)) + ')'
Set #CountNum = 0
END
SELECT #OldName = #NewName
FETCH NEXT FROM nameCursor INTO #NewName
Set #CountNum = #CountNum + 1
END
Print #OldName + ' (' + Cast(#CountNum as Varchar(50)) + ')'
CLOSE nameCursor
DEALLOCATE nameCursor
My solution just for kicks (this was a fun exercise), no cursors, no iterations, but i do have a helper field
-- Setup test table
DECLARE #names TABLE (
id INT IDENTITY(1,1),
name NVARCHAR(25) NOT NULL,
grp UNIQUEIDENTIFIER NULL
)
INSERT #names (name)
SELECT 'Harry Johns' UNION ALL
SELECT 'Adam Taylor' UNION ALL
SELECT 'John Smith' UNION ALL
SELECT 'John Smith' UNION ALL
SELECT 'Bill Manning' UNION ALL
SELECT 'Bill Manning' UNION ALL
SELECT 'Bill Manning' UNION ALL
SELECT 'John Smith' UNION ALL
SELECT 'Bill Manning'
-- Set the first id's group to a newid()
UPDATE n
SET grp = newid()
FROM #names n
WHERE n.id = (SELECT MIN(id) FROM #names)
-- Set the group to a newid() if the name does not equal the previous
UPDATE n
SET grp = newid()
FROM #names n
INNER JOIN #names b
ON (n.ID - 1) = b.ID
AND ISNULL(b.Name, '') <> n.Name
-- Set groups that are null to the previous group
-- Keep on doing this until all groups have been set
WHILE (EXISTS(SELECT 1 FROM #names WHERE grp IS NULL))
BEGIN
UPDATE n
SET grp = b.grp
FROM #names n
INNER JOIN #names b
ON (n.ID - 1) = b.ID
AND n.grp IS NULL
END
-- Final output
SELECT MIN(id) AS id_start,
MAX(id) AS id_end,
name,
count(1) AS consecutive
FROM #names
GROUP BY grp,
name
ORDER BY id_start
/*
Results:
id_start id_end name consecutive
1 1 Harry Johns 1
2 2 Adam Taylor 1
3 4 John Smith 2
5 7 Bill Manning 3
8 8 John Smith 1
9 9 Bill Manning 1
*/
Well, this:
select Name, count(Id)
from MyTable
group by Name
will give you this:
Harry Johns, 1
Adam Taylor, 1
John Smith, 2
Bill Manning, 1
and this (MS SQL syntax):
select Name +
case when ( count(Id) > 1 )
then ' ('+cast(count(Id) as varchar)+')'
else ''
end
from MyTable
group by Name
will give you this:
Harry Johns
Adam Taylor
John Smith (2)
Bill Manning
Did you actually want that other John Smith on the end of your results?
EDIT: Oh I see, you want consecutive runs grouped. In that case, I'd say you need a cursor or to do it in your program code.
How about this:
declare #tmp table (Id int, Nm varchar(50));
insert #tmp select 1, 'Harry Johns';
insert #tmp select 2, 'Adam Taylor';
insert #tmp select 3, 'John Smith';
insert #tmp select 4, 'John Smith';
insert #tmp select 5, 'Bill Manning';
insert #tmp select 6, 'John Smith';
select * from #tmp order by Id;
select Nm, count(1) from
(
select Id, Nm,
case when exists (
select 1 from #tmp t2
where t2.Nm=t1.Nm
and (t2.Id = t1.Id + 1 or t2.Id = t1.Id - 1))
then 1 else 0 end as Run
from #tmp t1
) truns group by Nm, Run
[Edit] That can be shortened a bit
select Nm, count(1) from (select Id, Nm, case when exists (
select 1 from #tmp t2 where t2.Nm=t1.Nm
and abs(t2.Id-t1.Id)=1) then 1 else 0 end as Run
from #tmp t1) t group by Nm, Run
For this particular case, all you need to do is group by the name and ask for the count, like this:
select Name, count(*)
from MyTable
group by Name
That'll get you the count for each name as a second column.
You can get it all as one column by concatenating like this:
select Name + ' (' + cast(count(*) as varchar) + ')'
from MyTable
group by Name