我已经看到很多关于旋转和其他解决方案的答案,用于将1:M关系从多行转换为多列,但我还没有看到一个解决我的特定场景的问题。我正在使用SQL Server 2014。
我有两张桌子:参加者和AttendeeChild。以下是每个中的相关列:
编号
标识
AttendeeId
年龄
名称
性别
参加者可以拥有任意数量的孩子"行。对于每个与会者,我想将AttendeeChild行展平为列,以便输出看起来像AttendeeId = 1;
AttendeeId Child1Age Child1Name Child1Gender Child2Age Child2Name Child2Gender
---------- --------- ---------- ------------ --------- ---------- ------------
1 10 Sam Boy 9 Sally Girl
子相关属性集应该继续扩展每个AttendeeChild行的列数,列标题应该保留模式" Child {#} {Attribute}"其中{#}是来自AttendeeChild的下一个子行的递增计数器,{Attribute}是" Name"," Age"或"性别"。
我希望这是传达问题的足够信息。
答案 0 :(得分:0)
在制作动态SQL时使用此模板作为模板:
WITH ChildNumbered AS
(
SELECT
AttendeeID,
Age,
Name,
Gender,
ROW_NUMBER() OVER (PARTITION BY AttendeeID ORDER BY ID) AS RN
)
SELECT
A.ID as AttendeeId,
C1.Age AS Child1Age, C1.Name AS Child1Name, C1.Gender AS Child1Gender,
C2.Age AS Child2Age, C2.Name AS Child2Name, C2.Gender AS Child2Gender,
-- ...
CX.Age AS ChildXAge, CX.Name AS ChildXName, CX.Gender AS ChildXGender,
CY.Age AS ChildYAge, CY.Name AS ChildYName, CY.Gender AS ChildYGender,
CZ.Age AS ChildZAge, CZ.Name AS ChildZName, CZ.Gender AS ChildZGender
FROM Attendee AS A
LEFT JOIN ChildNumbered AS C1 ON A.ID = C1.AttendeeID AND C1.RN = 1
LEFT JOIN ChildNumbered AS C2 ON A.ID = C2.AttendeeID AND C2.RN = 2
-- ...
LEFT JOIN ChildNumbered AS CX ON A.ID = CX.AttendeeID AND CX.RN = X
LEFT JOIN ChildNumbered AS CY ON A.ID = CY.AttendeeID AND CY.RN = Y
LEFT JOIN ChildNumbered AS CZ ON A.ID = CZ.AttendeeID AND CZ.RN = Z
答案 1 :(得分:0)
另一种选择是使用一些XML将数据转换为EAV结构(实体属性值)
这种方法非常动态,会产生适当数量的"组"按照正确的顺序。
Declare @AttendeeChild table (Id int,AttendeeId int,Age Int,Name varchar(50),Gender varchar(25))
Insert Into @AttendeeChild values
(1,1,10,'Sam','Boy'),
(2,1,9,'Sally','Girl'),
(2,2,9,'Sue','Boy')
-- Convert Data to EAV Structure'ish
Declare @XML xml = (Select *,GrpSeq=Row_Number() over (Partition By AttendeeId Order By Age Desc) from @AttendeeChild for XML RAW)
Select AttendeeId = r.value('@AttendeeId','int')
,GrpSeq = r.value('@GrpSeq','int')
,ColSeq = Row_Number() over (Partition By r.value('@AttendeeId','int') Order By (Select NULL))
--,Item = attr.value('local-name(.)','varchar(100)')
,Element = 'Child'+r.value('@GrpSeq','varchar(10)')+attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
Into #Temp
From @XML.nodes('/row') as A(r)
Cross Apply A.r.nodes('./@*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','AttendeeId','GrpSeq')
-- Get Cols in correct Order
Declare @Cols varchar(max) = Stuff((Select ',' + QuoteName(Element)
From (Select Distinct Top 100 Percent ColSeq,Element From #Temp Order By ColSeq ) A
For XML Path(''), Type
).value('.', 'varchar(max)'),1,1,'')
-- Execute Dynamic Pivot
Declare @SQL varchar(max) = '
Select *
From (Select AttendeeId,Element,Value From #Temp) T
Pivot (
max(Value)
For [Element] in (' + @Cols + ')
) P '
Exec(@SQL)
返回