I have two tables:
Departments --< Employees
Department table has data such as:
id, Name
1, Marketing
2, Sales
Employees table has data such as:
id, Name, DepartmentId, RatePerDay
1, Alex,1, 40
2, Bob,1, 30
3, Calvin,1, 40
4, Dal,1, 30
I want to get two data sets back as follows for each department:
DepartmentName, Employee1,Employee12,Employee13,Employee14
Marketing, Alex, Bob, Calvin, Dal
DepartmentName, RatePerDay1, RatePerDay2, RatePerDay3, RatePerDay4
Marketing, 40,30,40,30
I have to write a MS SQL 2008 Stored procedure which achieves this result?
Any help is appreciated
Ok, First take a look at this link, since you are gonna need dynamic SQL. Then you can try the following:
DECLARE #EmployeesId VARCHAR(MAX), #EmployeesIdAlias VARCHAR(MAX), #Query1 VARCHAR(MAX), #Query2 VARCHAR(MAX)
DECLARE #Rates VARCHAR(MAX), #RatesAlias VARCHAR(MAX)
SELECT #EmployeesId = ISNULL(#EmployeesId + ',', '') + '[' + CAST(Id AS VARCHAR(10)) + ']',
#EmployeesIdAlias = ISNULL(#EmployeesIdAlias + ',', '') + '[' + CAST(Id AS VARCHAR(10)) + '] AS [Employee ' + CAST(Id AS VARCHAR(10)) + ']',
#RatesAlias = ISNULL(#RatesAlias + ',', '') + '[' + CAST(Id AS VARCHAR(10)) + '] AS [Rate ' + CAST(Id AS VARCHAR(10)) + ']'
FROM Employees
SET #Query1 = '
SELECT Department, '+#EmployeesIdAlias+'
FROM ( SELECT A.Id, A.Name, B.Name Department
FROM Employees A
INNER JOIN Department B
ON A.DepartmentId = B.Id) Source
PIVOT(MIN(Name) FOR Id IN ('+#EmployeesId+')) AS PT'
EXEC(#Query1)
SET #Query2 = '
SELECT Department, '+#RatesAlias+'
FROM ( SELECT A.Id, A.RatePerDay, B.Name Department
FROM Employees A
INNER JOIN Department B
ON A.DepartmentId = B.Id) Source
PIVOT(MIN(RatePerDay) FOR Id IN ('+#EmployeesId+')) AS PT'
EXEC(#Query2)
Pivot may work for what your doing:
http://msdn.microsoft.com/en-us/library/ms177410.aspx
http://archive.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=PIVOTData
Related
I am working to recreate some Excel pivots in SQL Server, which is requiring the use of Coalesce functions. Referring to online documentation like the following:
Row and column total in dynamic pivot
https://www.codeproject.com/Articles/232181/SQL-Pivot-with-Grand-Total-Column-and-Row
I have been able to create a count total with grand total column and row, along with a dynamic pivot counting open contacts (which is based on the primary table id) by user and workflow.
CREATE PROCEDURE [dbo].[RetrieveTotalOpenContacts]
#team_id int
AS
/* TEAM SPECIFIC PARAMETER */
DECLARE #team_string varchar(1)
SET #team_string = CONVERT(varchar(10), #team_id)
/* COLUMNS HEADERS */
DECLARE #columnHeaders NVARCHAR (MAX)
SELECT #columnHeaders = COALESCE (#columnHeaders
+ ',[' + workflow + ']', '[' + workflow + ']')
FROM contact_workflow
WHERE team_id = #team_id
GROUP BY workflow
ORDER BY workflow
/* GRAND TOTAL COLUMN */
DECLARE #GrandTotalCol NVARCHAR (MAX)
SELECT #GrandTotalCol = COALESCE (#GrandTotalCol + 'ISNULL ([' + CAST (workflow AS VARCHAR) +'],0) + ', 'ISNULL([' + CAST(workflow AS VARCHAR)+ '],0) + ')
FROM contact_workflow
WHERE team_id = #team_id
GROUP BY workflow
ORDER BY workflow
SET #GrandTotalCol = LEFT (#GrandTotalCol, LEN (#GrandTotalCol)-1)
/* GRAND TOTAL ROW */
DECLARE #GrandTotalRow NVARCHAR(MAX)
SELECT #GrandTotalRow = COALESCE(#GrandTotalRow + ',ISNULL(SUM([' +
CAST(workflow AS VARCHAR)+']),0)', 'ISNULL(SUM([' + CAST(workflow AS VARCHAR)+']),0)')
FROM contact_workflow
WHERE team_id = #team_id
GROUP BY workflow
ORDER BY workflow
/* MAIN QUERY */
DECLARE #FinalQuery NVARCHAR (MAX)
SET #FinalQuery = 'SELECT *, (' + #GrandTotalCol + ')
AS [Grand Total] INTO #temp_MatchesTotal
FROM
(SELECT
o.full_name AS [User],
w.workflow AS [Workflow],
c.id AS [Contacts]
FROM
contact c
INNER JOIN app_user o ON c.owner_id = o.id
INNER JOIN contact_workflow w ON c.workflow_id = w.id
WHERE
c.resolver_id IS NULL
AND
o.active_indicator = 1
AND
o.team_id = ' + #team_string + '
AND
w.team_id = ' + #team_string + '
) A
PIVOT
(
count ([Contacts])
FOR [Workflow]
IN (' +#columnHeaders + ')
) B
SELECT * FROM #temp_MatchesTotal UNION ALL
SELECT ''Grand Total'' ,'+#GrandTotalRow +',
ISNULL (SUM([Grand Total]),0) FROM #temp_MatchesTotal
DROP TABLE #temp_MatchesTotal'
EXECUTE(#FinalQuery)
This count aggregation, along with the grand total column and row, is working. However, I am trying to generate a minimum aggregation on a date/time field. I'm not used to this matrix-style of thinking, but I managed to get the body of the cross tabulation to show the minimum dates for plan_stamp based on a min pivot on workflow.
CREATE PROCEDURE [dbo].[RetrieveOldestOpenContacts]
#team_id int
AS
/* TEAM SPECIFIC PARAMETER */
DECLARE #team_string varchar(1)
SET #team_string = CONVERT(varchar(10), #team_id)
/* COLUMNS HEADERS */
DECLARE #columnHeaders NVARCHAR (MAX)
SELECT #columnHeaders = COALESCE (#columnHeaders
+ ',[' + workflow + ']', '[' + workflow + ']')
FROM contact_workflow
WHERE team_id = #team_id
GROUP BY workflow
ORDER BY workflow
/* GRAND TOTAL COLUMN */
DECLARE #GrandTotalCol NVARCHAR (MAX)
SELECT #GrandTotalCol = COALESCE (#GrandTotalCol + 'ISNULL([' + CAST (workflow AS VARCHAR) +'],0) + ', 'ISNULL([' + CAST(workflow AS VARCHAR)+ '],0) + ')
FROM contact_workflow
WHERE team_id = #team_id
GROUP BY workflow
ORDER BY workflow
SET #GrandTotalCol = LEFT (#GrandTotalCol, LEN (#GrandTotalCol)-1)
/* GRAND TOTAL ROW */
DECLARE #GrandTotalRow NVARCHAR(MAX)
SELECT #GrandTotalRow = COALESCE(#GrandTotalRow + ',ISNULL(min([' +
CAST(workflow AS VARCHAR)+']),null)', 'ISNULL(min([' + CAST(workflow AS VARCHAR)+']),null)')
FROM contact_workflow
WHERE team_id = #team_id
GROUP BY workflow
ORDER BY workflow
/* MAIN QUERY */
DECLARE #FinalQuery NVARCHAR (MAX)
SET #FinalQuery = 'SELECT *, (' + #GrandTotalCol + ')
AS [Grand Total] INTO #temp_MatchesTotal
FROM
(SELECT
o.full_name AS [User],
w.workflow AS [Workflow],
c.plan_stamp AS [Contacts]
FROM
contact c
INNER JOIN app_user o ON c.owner_id = o.id
INNER JOIN contact_workflow w ON c.workflow_id = w.id
WHERE
c.resolver_id IS NULL
AND
o.active_indicator = 1
AND
o.team_id = ' + #team_string + '
AND
w.team_id = ' + #team_string + '
) A
PIVOT
(
min ([Contacts])
FOR [Workflow]
IN (' +#columnHeaders + ')
) B
SELECT * FROM #temp_MatchesTotal UNION ALL
SELECT ''Grand Total'' ,'+#GrandTotalRow +',
ISNULL (min([Grand Total]),null) FROM #temp_MatchesTotal
DROP TABLE #temp_MatchesTotal'
EXECUTE(#FinalQuery)
The grand total row is working appropriately, but NOT the grand total column. Instead of the minimum value of the row, the grand total column is returning the date values added together.
I need to fix this if possible, so I can show the oldest date (i.e. the min) for that particular user (i.e. the grand total column). I don't quite understand how to accomplish that modification, either using Coalesce or something else.
UPDATE: Casting the date field as a datetime type, to avoid it being read as a string, did not fix the issue. The grand total column still adds the values together, rather than finding the lowest. Please see result here.CAST(c.plan_stamp AS datetime)
I have below query written for my attendance report. everything works fine except one thing. i have multiple checkin/checkout allowed in a day in my application now when i run my query it returns checkin, checkout, total, checkin checkout total. the result i expect is something like: checkin, checkout, checkin, checkout.... total.
Below is the query:
SELECT EmployeeID, Employee, [2016-09-01],[2016-09-02],[2016-09-05],[2016-09-06],[2016-09-07],[2016-09-08],[2016-09-09] from
(
SELECT src.EmployeeID, isnull(b.EmployeeName,'') +' '+ isnull(b.LastName,'') Employee
,[CheckinDate]
, ISNULL(CAST(c.LeaveDesc as VARCHAR(max)), STUFF((
SELECT ', ' + CAST(ISNULL(CheckinTime,'') AS VARCHAR(5)) + char(10) + CAST(ISNULL(CheckoutTime,'') AS VARCHAR(5)) +
char(10) + CAST(ISNULL(TotalHours,'') AS VARCHAR(5))
FROM EmployeeDetail
WHERE (EmployeeID = src.EmployeeID and CheckinDate = src.CheckinDate)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'')) AS Result
FROM [EmployeeDetail] as src inner join EmployeeMaster b on src.EmployeeID = b.EmployeeID and src.KindergardenID = b.KindergardenID
left outer join leavetype c on src.leaveid = c.leaveid
WHERE src.KindergardenID = 1
GROUP BY src.EmployeeID, isnull(b.EmployeeName,'') +' '+ isnull(b.LastName,''), CheckinDate,LeaveDesc
) x
pivot
(
max(Result)
for CheckinDate in ([2016-09-01],[2016-09-02],[2016-09-05],[2016-09-06],[2016-09-07],[2016-09-08],[2016-09-09])
) p
Any help in changing the query to make it work as expected is appreciated.
Let me know if i was confusing in asking.
SELECT EmployeeID, Employee, ' + #cols + ' from
(
SELECT src.EmployeeID, isnull(b.EmployeeName,'''') +'' ''+ isnull(b.LastName,'''') Employee
,[CheckinDate]
, ISNULL(CAST(c.LeaveDesc as VARCHAR(max)), isnull(left(convert(time,DATEADD(minute,(SUM(DATEDIFF(MINUTE, ''0:00:00'', TotalHours))),0)),5),''N/A'') + char(10) + STUFF((
SELECT '', '' + CAST(ISNULL(CheckinTime,'''') AS VARCHAR(5)) + ''-'' + CAST(ISNULL(CheckoutTime,'''') AS VARCHAR(5)) +
char(10)
FROM EmployeeDetail
WHERE (EmployeeID = src.EmployeeID and CheckinDate = src.CheckinDate)
FOR XML PATH(''''),TYPE).value(''(./text())[1]'',''VARCHAR(MAX)'')
,1,2,'''')) AS Result
FROM [EmployeeDetail] as src inner join EmployeeMaster b on src.EmployeeID = b.EmployeeID and src.KindergardenID = b.KindergardenID
left outer join leavetype c on src.leaveid = c.leaveid
WHERE src.KindergardenID = ' + CAST(#kindergardenid as varchar(max)) + '
GROUP BY src.EmployeeID, isnull(b.EmployeeName,'''') +'' ''+ isnull(b.LastName,''''), CheckinDate,LeaveDesc
) x
pivot
(
max(Result)
for CheckinDate in (' + #cols + ')
) p
Preamble: I've read through the three questions/answers here,here, and here, with big ups to #cade-roux. This all stemmed from trying to use the following data in a 2005 SSRS matrix that, I believe, doesn't work because I want to show a member having to take a test multiple times, and SSRS seems to require the aggregate where I want to show all dates.
I get the following results in my table, which seems to be showing all the data correctly:
How do I change the code below to show a) the "tests" at the top of each column with b) if it's called for, the multiple dates that test was taken?
Here's the code I have to produce the table, above. Much of it is commented out as I was just trying to get the pivot to work, but you may notice I am also trying to specify which test column comes first.
CREATE TABLE #tmp ( ---THIS WORKS BUT TESTS ARE VERTICAL
[TEST] [varchar](30) NOT NULL,
[ED] [datetime] NOT NULL
)
--WHERE THE TEST AND ED COME FROM
INSERT #TMP
SELECT DISTINCT
-- N.FULL_NAME
-- , CONVERT(VARCHAR(30), AM.CREATEDATE, 101) AS ACCOUNT_CLAIMED
-- , N.EMAIL
-- , NULL AS 'BAD EMAIL'
-- , CONVERT(VARCHAR(30), AC.EFFECTIVE_DATE, 101) AS EFFECTIVE_DATE
AC.PRODUCT_CODE AS TEST
, CONVERT(VARCHAR(30), AC.EFFECTIVE_DATE, 101) AS ED
-- , CASE
-- WHEN AC.PRODUCT_CODE = 'NewMem_Test' THEN '9'
-- WHEN AC.PRODUCT_CODE = 'NM_Course1' THEN '1'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course1' THEN '2'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course2' THEN '3'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course3' THEN '4'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course4' THEN '5'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course5' THEN '6'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course6' THEN '7'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course7' THEN '8'
-- END AS 'COLUMN_ORDER'
FROM NAME N
JOIN USERMAIN UM
ON N.ID = UM.CONTACTMASTER
JOIN formTransLog TL
ON UM.USERID = TL.USERNAME
JOIN anet_Users AU
ON UM.USERID = AU.USERNAME
JOIN anet_Membership AM
ON AU.USERID = AM.USERID
JOIN ACTIVITY AC
ON N.ID = AC.ID
AND AC.ACTIVITY_TYPE = 'COURSE'
AND AC.PRODUCT_CODE LIKE 'N%'
--ORDER BY 1, 7
DECLARE #sql AS varchar(max)
DECLARE #pivot_list AS varchar(max) -- Leave NULL for COALESCE technique
DECLARE #select_list AS varchar(max) -- Leave NULL for COALESCE technique
SELECT #pivot_list = COALESCE(#pivot_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + ']'
,#select_list = COALESCE(#select_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + '] AS [col_' + CONVERT(varchar, PIVOT_CODE) + ']'
FROM (
SELECT DISTINCT PIVOT_CODE
FROM (
SELECT TEST, ED, ROW_NUMBER() OVER (PARTITION BY TEST ORDER BY ED) AS PIVOT_CODE
FROM #tmp
) AS rows
) AS PIVOT_CODES
SET #sql = '
;WITH p AS (
SELECT TEST, ED, ROW_NUMBER() OVER (PARTITION BY TEST ORDER BY ED) AS PIVOT_CODE
FROM #tmp
)
SELECT TEST, ' + #select_list + '
FROM p
PIVOT (
MIN(ED)
FOR PIVOT_CODE IN (
' + #pivot_list + '
)
) AS pvt
'
PRINT #sql
EXEC (#sql)
EDIT:
The goal is to have the report in SSRS look like this:
I was able to produce the results you were looking for by adding in a number (RowNum) to the query underneath the PIVOT operator. It doesn't have to be in the final query (though you might want it for client-side sorting), but by having it in the underlying layer the PIVOT operation treats that number like a member of a GROUP BY clause.
Please look through my sample SQL below and let me know if this matches your criteria.
CREATE TABLE #TMP
(
Name VARCHAR(10),
Test VARCHAR(20),
EffectiveDate DATETIME
)
INSERT INTO #TMP (Name, Test, EffectiveDate)
SELECT 'Jane', 'NM_Course1', '01/17/2014' UNION
SELECT 'Jane', 'NMEP_Course1', '12/19/2013' UNION
SELECT 'Jane', 'NMEP_Course1', '12/20/2013' UNION
SELECT 'Jane', 'NMEP_Course2', '12/19/2013' UNION
SELECT 'Jane', 'NMEP_Course2', '12/22/2013' UNION
SELECT 'Jane', 'NMEP_Course2', '01/05/2014' UNION
SELECT 'John', 'NM_Course1', '01/17/2014' UNION
SELECT 'John', 'NMEP_Course1', '01/11/2014'
DECLARE #sql AS varchar(max)
DECLARE #pivot_list AS varchar(max) -- Leave NULL for COALESCE technique
DECLARE #select_list AS varchar(max) -- Leave NULL for COALESCE technique
SELECT #pivot_list = COALESCE(#pivot_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + ']'
,#select_list = COALESCE(#select_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + '] AS [col_' + CONVERT(varchar, PIVOT_CODE) + ']'
FROM (
SELECT DISTINCT PIVOT_CODE
FROM (
SELECT TEST AS PIVOT_CODE
FROM #tmp
) AS rows
) AS PIVOT_CODES
SET #sql = '
SELECT Name, ' + #select_list + '
FROM
(
SELECT b.Name, RowNum, b.EffectiveDate, b.TEST AS PIVOT_CODE
FROM
(
SELECT Name, Test, EffectiveDate, ROW_NUMBER() OVER (PARTITION BY NAME, TEST ORDER BY EffectiveDate) RowNum
FROM #Tmp
) b
) p
PIVOT (
MIN(EffectiveDate)
FOR PIVOT_CODE IN (
' + #pivot_list + '
)
) AS pvt
ORDER BY Name, RowNum
'
PRINT #sql
EXEC (#sql)
DROP TABLE #TMP
so I have an employee that can work in many companies so I have an n to n relationship, how can I obtain the companies that one employee works in, in just one row with sql?
example
table - employee
Employeeid employeename
1 mike
table company
companyId CompanyName
1 cocacola
2 nokia
3 intel
table employeeCompany
id employeeid companyid
1 1 1
2 1 2
3 1 3
I thought with this but canĀ“t
select Employeeid , companyid
from employeeCompany
where employeeid = 1
group by Employeeid , companyid
Easiest way to do it in Sql Server is by use of FOR XML PATH. The cryptic part .value('text()[1]','nvarchar(max)') handles special xml characters.
select employee.*, companies.*
from Employee
OUTER APPLY
(
select stuff ((SELECT ', ' + Company.CompanyName
FROM EmployeeCompany
INNER JOIN Company
ON EmployeeCompany.CompanyId = Company.CompanyID
WHERE EmployeeCompany.employeeid = Employee.EmployeeID
ORDER BY Company.CompanyName
FOR XML PATH(''),TYPE).value('text()[1]','nvarchar(max)')
, 1, 2, '') Companies
) companies
See demo at Sql Fiddle.
It sounds like you want something that is similar to mySQL's Group_Concat in SQL Server?
If you are looking for a way to do this so that each companyid is in a separate column, then that would only be possible with some difficulty using dynamic SQL. At which time it might be easier to just return this to an application and let it handle what it needs within its own logic?
BTW, the dynamic SQL logic would go something like this in case you were wondering...notice how nasty it is...thus why I would suggest against it.
select #highestCount = max(count(*))
from employeeCompany
group by Employeeid
declare createtemptable varchar(max), #filltableselect varchar(max), #filltablejoin varchar(max)
declare #currentCount int
set #currentCount = 0
set #createtemptable = 'CREATE TABLE #Temp (EmployeeID INT'
set #filltableselect = 'INSERT INTO #Temp SELECT EmployeeCompany0.EmployeeID, EmployeeCompany0.CompanyID'
set #filltablejoin = 'FROM EmployeeCompany AS EmployeeCompany0'
while(#currentCount < #highestCount)
begin
set #createtemptable = #createtemptable + ', CompanyID'
+ CAST(#currentCount AS VARCHAR(2)) + ' INT'
if(#currentCount > 0)
begin
set #filltableselect = #filltableselect + ', EmployeeCompany'
+ CAST(#currentCount AS VARCHAR(2)) + '.CompanyId'
set #filltablejoin = #filltablejoin
+ 'LEFT JOIN EmployeeCompany AS EmployeeCompany'
+ CAST(#currentCount AS VARCHAR(2))
+ ' ON EmployeeCompany0.EmployeeID = EmployeeCompany'
+ CAST(#currentCount AS VARCHAR(2)) + '.EmployeeID'
end
set #currentCount = #currentCount + 1
end
set #createtemptable = #createtemptable + ')'
--This next line can be whatever you need it to be
set #filltablejoin = #filltablejoin + 'WHERE employeeCompany0.EmployeeID = 1'
exec #createtemptable
exec #filltableselect + #filltablejoin
I am writing a query to get the address for PersonID. Following query is working for me but it only returns with two Addresses. I want to handle the 'n' number of address with a single query. Is there any way to do this?
Many thanks
SELECT
PersonID, PersonName
[Address1], [Address2]
FROM
(
SELECT
P.PersonID,
P.PersonName,
(ROW_NUMBER() OVER(PARTITION BY P.PersonID ORDER BY A.AddressID)) RowID
FROM tblPerson
INNER JOIN tblAddress AS A ON A.PersonID = P.PersonID
) AS AddressTable
PIVOT
(
MAX(AddressID)
FOR RowID IN ([Address1], [Address2])
) AS PivotTable;
Assuming the following tables and sample data:
USE tempdb;
GO
CREATE TABLE dbo.tblPerson(PersonID INT, PersonName VARCHAR(255));
INSERT dbo.tblPerson SELECT 1, 'Bob'
UNION ALL SELECT 2, 'Charlie'
UNION ALL SELECT 3, 'Frank'
UNION ALL SELECT 4, 'Amore';
CREATE TABLE dbo.tblAddress(AddressID INT, PersonID INT, [Address] VARCHAR(255));
INSERT dbo.tblAddress SELECT 1,1,'255 1st Street'
UNION ALL SELECT 2,2,'99 Elm Street'
UNION ALL SELECT 3,2,'67 Poplar Street'
UNION ALL SELECT 4,2,'222 Oak Ave.'
UNION ALL SELECT 5,1,'36 Main Street, Suite 22'
UNION ALL SELECT 6,4,'77 Sicamore Ct.';
The following query gets the results you want, and shows how it handles 0, 1 or n addresses. In this case the highest number is 3 but you can play with more addresses if you like by adjusting the sample data slightly.
DECLARE #col NVARCHAR(MAX) = N'',
#sel NVARCHAR(MAX) = N'',
#from NVARCHAR(MAX) = N'',
#query NVARCHAR(MAX) = N'';
;WITH m(c) AS
(
SELECT TOP 1 c = COUNT(*)
FROM dbo.tblAddress
GROUP BY PersonID
ORDER BY c DESC
)
SELECT #col = #col + ',[Address' + RTRIM(n.n) + ']',
#sel = #sel + ',' + CHAR(13) + CHAR(10) + '[Address' + RTRIM(n.n) + '] = x'
+ RTRIM(n.n) + '.Address',
#from = #from + CHAR(13) + CHAR(10) + ' LEFT OUTER JOIN xMaster AS x'
+ RTRIM(n.n) + ' ON x' + RTRIM(n.n) + '.PersonID = p.PersonID AND x'
+ RTRIM(n.n) + '.rn = ' + RTRIM(n.n)
FROM m CROSS JOIN (SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_columns) AS n WHERE n.n <= m.c;
SET #query = N';WITH xMaster AS
(
SELECT PersonID, Address,
rn = ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY Address)
FROM dbo.tblAddress
)
SELECT PersonID, PersonName' + #col
+ ' FROM
(
SELECT p.PersonID, p.PersonName, ' + STUFF(#sel, 1, 1, '')
+ CHAR(13) + CHAR(10) + ' FROM dbo.tblPerson AS p ' + #from + '
) AS Addresses;';
PRINT #query;
--EXEC sp_executesql #query;
If you print the SQL you will see this result:
;WITH xMaster AS
(
SELECT PersonID, Address,
rn = ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY Address)
FROM dbo.tblAddress
)
SELECT PersonID, PersonName,[Address1],[Address2],[Address3] FROM
(
SELECT p.PersonID, p.PersonName,
[Address1] = x1.Address,
[Address2] = x2.Address,
[Address3] = x3.Address
FROM dbo.tblPerson AS p
LEFT OUTER JOIN xMaster AS x1 ON x1.PersonID = p.PersonID AND x1.rn = 1
LEFT OUTER JOIN xMaster AS x2 ON x2.PersonID = p.PersonID AND x2.rn = 2
LEFT OUTER JOIN xMaster AS x3 ON x3.PersonID = p.PersonID AND x3.rn = 3
) AS Addresses;
If you execute it, you will see this:
I know the query to get here is an ugly mess, but your requirement dictates it. It would be easier to return a comma-separated list as I suggested in my comment, or to have the presentation tier deal with the pivoting.