SQL one to many on one row - sql

I have a table that records the diagnosis of patient for each attendance. Patients can have more than one diagnosis.
I want one row per attendance with all diagnosis (up to 12 diagnosis). The below brings back more than one row.
select
dg.AttendanceID
, dg.PatientNumber
, dg.DiagnosisDate
, dg.Diagnosis
from
Diagnosis dg
AttendanceID PatientNumber DiagnosisDate Diagnosis
10001 123456 01-Oct-13 A
10001 123456 01-Oct-13 B
10002 123456 20-Oct-13 D
It results should look like this:
AttendanceID PatientNumber DiagnosisDate Diagnosis 1 Diagnosis 2 Diagnosis 3
10001 123456 01-Oct-13 A B
10002 123456 20-Oct-13 D
Can someone please help?

You can get the result by implementing the PIVOT function, but I would also suggest using the windowing function row_number() to generate the number of diagnosis for each attendanceid and patientnumber:
select attendanceid, patientnumber,
diagnosisdate,
Diagnosis1, Diagnosis2, Diagnosis3
from
(
select attendanceid, patientnumber,
diagnosisdate, diagnosis,
'diagnosis'+
cast(row_number() over(partition by attendanceid, patientnumber
order by diagnosis) as varchar(2)) seq
from diagnosis
) d
pivot
(
max(diagnosis)
for seq in (Diagnosis1, Diagnosis2, Diagnosis3)
) piv;
See SQL Fiddle with Demo. Since you know you will have up to 12 diagnosis per patient/attendance you can easily hard code the query to add the additional columns.
But if you needed a dynamic version of the code, then you could use something similar to the following:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(seq)
from
(
select 'diagnosis'+
cast(row_number() over(partition by attendanceid, patientnumber
order by diagnosis) as varchar(2)) seq
from diagnosis
) d
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT attendanceid, patientnumber,
diagnosisdate,' + #cols + '
from
(
select attendanceid, patientnumber,
diagnosisdate, diagnosis,
''diagnosis''+
cast(row_number() over(partition by attendanceid, patientnumber
order by diagnosis) as varchar(2)) seq
from diagnosis
) x
pivot
(
max(diagnosis)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo
Both versions give a result:
| ATTENDANCEID | PATIENTNUMBER | DIAGNOSISDATE | DIAGNOSIS1 | DIAGNOSIS2 | DIAGNOSIS3 |
|--------------|---------------|--------------------------------|------------|------------|------------|
| 10001 | 123456 | October, 01 2013 00:00:00+0000 | A | B | (null) |
| 10002 | 123456 | October, 20 2013 00:00:00+0000 | D | (null) | (null) |

I am not 100% sure but it should be something along those lines.
;with cte as(
select
dg.AttendanceID
, dg.PatientNumber
, dg.DiagnosisDate
, dg.Diagnosis
,ROW_NUMBER() over (partition by dg.attendanceID order by attendanceID) as seq1
,ROW_NUMBER() over (partition by dg.attendanceID, patient_no order by diagnosis) as seq2
from
Diagnosis dg
)
select
t1.attendanceID
,t1.patientNumber
,t1.diagnosisDate
,(select Diagnosis from cte t2 where t1.attendanceID=t2.attendanceID and t1.patientNumber=t2.patientNumber and t2.seq2='1') as diag1
,(select Diagnosis from cte t2 where t1.attendanceID=t2.attendanceID and t1.patientNumber=t2.patientNumber and t2.seq2='2') as diag2
,(select Diagnosis from cte t2 where t1.attendanceID=t2.attendanceID and t1.patientNumber=t2.patientNumber and t2.seq2='3') as diag3
,(select Diagnosis from cte t2 where t1.attendanceID=t2.attendanceID and t1.patientNumber=t2.patientNumber and t2.seq2='4') as diag4
,(select Diagnosis from cte t2 where t1.attendanceID=t2.attendanceID and t1.patientNumber=t2.patientNumber and t2.seq2='5') as diag5
from cte t1
where seq1= 1

Below is a probable solution by using cursors i am not sure if you can show it as columns as it is variable for every patient but this will show diagnosis as comma separated in one single column
DECLARE #combinedString VARCHAR(MAX),
#id int,
#Diagnosis VARCHAR(MAX)
Declare #CONCATRESULT TABLE (AttendanceID int, Question VARCHAR(MAX) )
Declare Dia cursor for
SELECT AttendanceID FROM Diagnosis
open Dia
Fetch next from Dia into #id
while ##FETCH_STATUS=0
begin
SELECT #combinedString = COALESCE(#combinedString + ', ', '') + Diagnosis,
#id=AttendanceID FROM [Prod_PostData].[dbo].[KMSFPostDataDenorm] d
WHERE AttendanceID = #id
insert into #CONCATRESULT values ( #id ,#combinedString )
SET #combinedString = null
Fetch next from Dia into #id
end
close Dia
deallocate Dia
select
dg.AttendanceID
, dg.PatientNumber
, dg.DiagnosisDate
, dg.Diagnosis
from
Diagnosis dg join (select * from #CONCATRESULT) Y on dg.AttendanceID=Y.AttendanceID

Related

Dynamic Cols Pivot Rows but only some columns, not all

I'm making a dynamic pivot table with a similar structure in the example below.
Query to list each child record in columns of a parent
EXCEPT, all the examples I seem to find, when building the list of dynamics columns for the pivot, they all use "quotename" to get all columns in the table, while I only want a handful for my output.
select #colsPivot = STUFF((SELECT ','
+ quotename(c.name +'_'+ cast(t.rn as varchar(10)))
from
(
select cast(row_number() over(partition by m.MemberID order by g.guestid) as varchar(50)) rn
from member m
left join guest g
on m.guestid = g.guestid
) t
cross apply sys.columns as C
where C.object_id = object_id('guest')
group by c.name, t.rn
order by t.rn
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Wondering if anyone can help with the syntax. In the above example, instead of getting First name and Last Name, hypothetically, I only want Last Name. How would that look?
I'm just looking at how to replace the quotename properly, I believe I have the rest running properly.
SAMPLE:
Member Table
MemberID | FName | LName
001 Frank Smith
002 Mary Jane
003 John Henry
Guest Table
GuestID | FName | LName | MemberId
101 Steve Smith | 001
102 Peter Smith | 001
103 Mike Jane | 002
OUTPUT:
MemberID | FName | LName| GuestID1 | LName1 |GuestID2 | LName2
001 Frank Smith 101 Smith 102 Smith
002 Mary Jane 103 Jane
003 John Henry
Any and all help is much appreciated!
Key point is that we need to use row_number window function to make a row number and then use condition aggregate function to make it.
SELECT MemberID,FName,LName,
MAX(CASE WHEN rn = 1 THEN GuestID END) GuestID1,
MAX(CASE WHEN rn = 1 THEN g_LName END) LName1,
MAX(CASE WHEN rn = 2 THEN GuestID END) GuestID2,
MAX(CASE WHEN rn = 2 THEN g_LName END) LName2
FROM (
SELECT m.MemberID,
m.FName,
m.LName,
GuestID,
g.LName g_LName,
ROW_NUMBER() OVER(PARTITION BY m.MemberID ORDER BY GuestID) rn
FROM Member m LEFT JOIN Guest g
ON m.MemberId = g.MemberId
) t1
GROUP BY MemberID,FName,LName
if there might be multiple LName (more than 2) and you want to use Dynamic pivot you can try the below code, but that might be a little complex.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((
SELECT distinct ', MAX(CASE WHEN rn = '+ CAST(t1.cnt AS VARCHAR(5)) + ' THEN GuestID END)' + ' as '''+CONCAT(name,t1.cnt)+''''
FROM (SELECT COUNT(*) cnt FROM Guest GROUP BY MemberId) t1
CROSS JOIN (SELECT 'GuestID' name UNION ALL SELECT 'LName') t2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT MemberID,FName,LName, ' + #cols + '
FROM (
SELECT m.MemberID,
m.FName,
m.LName,
GuestID,
g.LName g_LName,
ROW_NUMBER() OVER(PARTITION BY m.MemberID ORDER BY GuestID) rn
FROM Member m LEFT JOIN Guest g
ON m.MemberId = g.MemberId
) t1
GROUP BY MemberID,FName,LName '
execute(#query)
sqlfiddle

Dynamic Way To Transpose SQL Table Similar to Excel Functionality

I've looked around a good amount and haven't found quite the correct solution to what I think is a very simple problem. Everything I find focuses on one value being tranposed, but here I have a bunch.
I have this table currently and I want to transpose it exactly how Excel would. What is the easiest way to do this?
What I have:
nodeid | Headcount | FTE | Count of Part-Time Employees | Count of Non-Permanent Employees |
Actuals | 12 | 13 | 23 | 35 |
Targets | 15 | 15 | 25 | 25 |
What I want:
nodeid | Actuals | Targets |
Headcount | 12 | 15 |
FTE | 13 | 15 |
Count of Part-Time Employees | 23 | 25 |
Count of Non-Permanent Employees | 35 | 25 |
If 2016+, you can use a bit of JSON to "dynamically" unpivot your data, and then simply apply a PIVOT
Example
Declare #YourTable Table ([nodeid] varchar(50),[Headcount] int,[FTE] int,[Count of Part-Time Employees] int,[Count of Non-Permanent Employees] int) Insert Into #YourTable Values
('Actuals',12,13,23,35)
,('Targets',15,15,25,25)
Select *
From (
Select A.NodeID
,B.*
From #YourTable A
Cross Apply (
Select [Node] = [Key]
,[Value] = try_convert(int,Value)
From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper )) ) B
Where try_convert(int,value) is not null
) src
Pivot ( max(Value) for [NodeID] in ([Actuals],[Targets]) ) pvt
Order By Node desc
Returns
Node Actuals Targets
Headcount 12 15
FTE 13 15
Count of Part-Time Employees 23 25
Count of Non-Permanent Employees 35 25
Note: If <2016 there is an similar XML approach.
You can use unpivot and pivot to achieve that.
try the following:
DECLARE #tab TABLE
(nodeid VARCHAR(100),
Headcount INT,
FTE INT,
[Count of Part-Time Employees] INT,
[Count of Non-Permanent Employees] INT
);
INSERT INTO #tab SELECT 'Actuals', 12, 13, 23, 35;
INSERT INTO #tab SELECT 'Targets', 15, 15, 25, 25;
SELECT *FROM #tab;
DROP TABLE IF EXISTS #temp;
SELECT *
INTO #temp
FROM
(
SELECT nodeid,
Headcount,
FTE,
[Count of Part-Time Employees],
[Count of Non-Permanent Employees]
FROM #tab
) t UNPIVOT(val FOR col IN(Headcount,
FTE,
[Count of Part-Time Employees],
[Count of Non-Permanent Employees])) upvt;
DECLARE #collist VARCHAR(MAX);
SELECT #collist = STUFF(
(
SELECT DISTINCT ',' + QUOTENAME(nodeid)
FROM #temp
ORDER BY ',' + QUOTENAME(nodeid) FOR XML PATH('')
), 1, 1, '');
DECLARE #sql VARCHAR(MAX)= 'select col as nodeid, Actuals, Targets
from #temp t
pivot
(
max(val) for nodeid IN (' + #collist + ')
)pvt';
EXEC (#sql);
Please see the db<>fiddle here

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

SQL Server 2008 Management Studio : convert row data into columns [duplicate]

This question already has answers here:
Transpose rows and columns with no aggregate
(2 answers)
Closed 8 years ago.
I have tried viewing other posts on the subject but all the examples I've seen are based on knowing a specific value.
Example of what I have:
Address Name Number
------- ------- -------
1234 Main Bob 555-555-5555
1234 Main Karen 444-444-4444
1990 Maple Susie 333-333-3333
1010 12th Joe 222-222-2222
1010 12th Beth 111-111-1111
1010 12th Steve 444-433-3221
Example of what I want:
Address Contact1 Contact2 Contact3
------- ------- -------- --------
1234 Main Bob:555-555-5555 Karen:444-444-4444 NULL
1990 Maple Susie:333-333-3333 NULL NULL
1010 12th Joe: 222-222-2222 Beth 111-111-1111 Steve 444-433-3221
There are thousands of rows so I can't CASE and.. I'm a more than a little lost here.
Any suggestions?
I think you can use the following query. This assumes you have maximum three contacts
;WITH orderedAddress(Address,Name,Number,Sort)
AS
(
SELECT Address,Name,Number,ROW_NUMBER() OVER(PARTITION BY Address ORDER BY Name) AS num
FROM Addresses
)
SELECT main.Address,Contact1.Name + ':' + Contact1.Number AS Contact1 ,
Contact2.Name + ':' + ISNULL(Contact2.Number,'') AS Contact2 ,
Contact3.Name + ':' + ISNULL(Contact3.Number,'') AS Contact3
FROM
(SELECT DISTINCT Address FROM Addresses) AS main
LEFT JOIN OrderedAddress Contact1 ON main.Address = Contact1.Address AND Contact1.Sort=1
LEFT JOIN OrderedAddress Contact2 ON main.Address = Contact2.Address AND Contact2.Sort=2
LEFT JOIN OrderedAddress Contact3 ON main.Address = Contact3.Address AND Contact3.Sort=3
A dynamic pivot will work but so will a dynamic cross tab. Generally speaking a cross tab will beat the pivot for performance. Here is an example of one I posted just a few days ago.
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

Pivot Table with many to many table

My SQL Fiddle is here: http://sqlfiddle.com/#!3/d5c60
CREATE TABLE customer
(
id int identity primary key,
name varchar(20),
);
CREATE TABLE warehouse
(
id int identity primary key,
name varchar(20),
);
CREATE TABLE customerwarehouse
(
id int identity primary key,
customerid int,
warehouseid int
);
INSERT INTO customer (name)
VALUES
('CustA'),
('CustB'),
('CustC');
INSERT INTO warehouse (name)
VALUES
('wh01'),
('wh02'),
('wh03');
INSERT INTO customerwarehouse (customerid, warehouseid)
VALUES
(1,1),
(2,1),
(2,2),
(3,1),
(3,2),
(3,3);
I would like to write a query to return the customer/warehouse data in the following format:
Customer WH1 WH2 WH3
CustA wh01
CustB wh01 wh02
CustC wh01 wh02 wh03
My attempt to do this returns null for all warehouses.
How can I construct my query to return the data in the required format?
In order to get the result, you will want to JOIN the tables and apply the PIVOT function. I would also suggest using the row_number() windowing function to get the number of warehouses for each customer - this will be the value that will be used as your new column headers.
select customername, wh1, wh2, wh3
from
(
select w.name warehousename,
c.name customername,
'wh'+cast(row_number() over(partition by c.id
order by w.id) as varchar(10)) seq
from customer c
inner join customerwarehouse cw
on c.id = cw.customerid
inner join warehouse w
on cw.warehouseid = w.id
) d
pivot
(
max(warehousename)
for seq in (wh1, wh2, wh3)
) piv;
See SQL Fiddle with Demo. If you have an unknown number of values, then you will need to use dynamic SQL to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('wh'+cast(row_number() over(partition by customerid
order by warehouseid) as varchar(10)))
from customerwarehouse
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT customername, ' + #cols + '
from
(
select w.name warehousename,
c.name customername,
''wh''+cast(row_number() over(partition by c.id
order by w.id) as varchar(10)) seq
from customer c
inner join customerwarehouse cw
on c.id = cw.customerid
inner join warehouse w
on cw.warehouseid = w.id
) x
pivot
(
max(warehousename)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. Both give a result:
| CUSTOMERNAME | WH1 | WH2 | WH3 |
| CustA | wh01 | (null) | (null) |
| CustB | wh01 | wh02 | (null) |
| CustC | wh01 | wh02 | wh03 |
Here's what I came up with after viewing the Complex PIVOT Example on this MSDN page:
SELECT
CustomerName,
case when [wh01] is null then null else 'wh01' end,
case when [wh02] is null then null else 'wh02' end,
case when [wh03] is null then null else 'wh03' end
FROM (
SELECT
c.Name AS CustomerName,
cw.id AS cwid,
w.name AS WarehouseName
FROM Customer c
JOIN CustomerWarehouse cw
ON c.id = cw.customerId
JOIN Warehouse w
ON w.id = cw.warehouseId
) AS SourceTable
pivot (
max(cwid)
FOR WarehouseName IN (
[wh01], [wh02], [wh03]
)
) AS PivotTable
On SQLFiddle: http://sqlfiddle.com/#!3/d5c60/42