我有一个非常宽的表非规范化(如果可以这么说),像这样,选项列转到100 +
Year ProductID ProductName Option1 Option2 Option3 ....Option100
-----------------------------------------------------------------
2016 1 Test1 A1 A1a A3
2015 1 Test1 A1 A2 A2a
问题是我们有动态查询尝试确定选项列,然后在其中找到选项值
即
@SQL= 'SELECT Option' + @getOptionNum +' FROM ProductMapping
理想情况下,我希望将此转换为此类
Year ProductID ProductName OptionName
-------------------------------------
2016 1 Test1 Option1
2016 1 Test1 Option2
2016 1 Test1 Option3
2015 1 Test1 Option1
2015 1 Test1 Option2
2015 1 Test1 Option3
OptionID OptionName OptionValue Year
-------------------------------------
1 Option1 A1 2016
2 Option2 A1a 2016
3 Option3 A3 2016
4 Option1 A1 2015
5 Option2 A2 2015
6 Option3 A2a 2015
SELECT *
FROM ProductMapping map
LEFT JOIN OptionList list ON map.OptionName = list.OptionName
AND map.Year = list.Year
AND map.OptionName = 'Option1'
我遇到的问题是如何通过查询将宽表转换为两个表结构,因为它有很多列和行,我不能手动将所有这些都归一化。
是的,理想情况下我还理解第二个表需要进一步规范化,以便将Option1 ... Option3保存在一个单独的表中,将Option1..A1映射保存在一个单独的表中,但这是一个开始......
希望这个简单的例子揭示了以下事实
有什么想法吗?
答案 0 :(得分:3)
Declare @YourTable table (Year int,ProductID int,ProductName varchar(50),Option1 varchar(50),Option2 varchar(50),Option3 varchar(50))
Insert into @YourTable values
(2016,1,'Test1','A1','A1a','A3'),
(2015,1,'Test1','A1','A2','A2a')
Declare @XML xml
Set @XML = (Select * from @YourTable for XML RAW)
Select *
From (
Select Year = r.value('@Year','int')
,ProductID = r.value('@ProductID','int')
,ProductName = r.value('@ProductName','varchar(50)')
,OptionName = Attr.value('local-name(.)','varchar(100)')
From @XML.nodes('/row') as A(r)
Cross Apply A.r.nodes('./@*[local-name(.)!="ProductID"]') as B(Attr)
) A
Where OptionName Like 'Option%'
Order by Year Desc,OptionName
Select OptionID=Row_Number() over (Order By Year Desc,OptionName),*
From (
Select OptionName = Attr.value('local-name(.)','varchar(100)')
,OptionValue = Attr.value('.','varchar(100)')
,Year = r.value('@Year','int')
From @XML.nodes('/row') as A(r)
Cross Apply A.r.nodes('./@*[local-name(.)!="ProductID"]') as B(Attr)
--CROSS APPLY A.r.nodes('./@*') AS B(Attr)
) A
Where OptionName Like 'Option%'
返回
Year ProductID ProductName OptionName
2016 1 Test1 Option1
2016 1 Test1 Option2
2016 1 Test1 Option3
2015 1 Test1 Option1
2015 1 Test1 Option2
2015 1 Test1 Option3
和
OptionID OptionName OptionValue Year
1 Option1 A1 2016
2 Option2 A1a 2016
3 Option3 A3 2016
4 Option1 A1 2015
5 Option2 A2 2015
6 Option3 A2a 2015
修改强>
现在,如果你想要一个STRAIGHT Normalization
Declare @YourTable table (Year int,ProductID int,ProductName varchar(50),Option1 varchar(50),Option2 varchar(50),Option3 varchar(50))
Insert into @YourTable values
(2016,1,'Test1','A1','A1a','A3'),
(2015,1,'Test1','A1','A2','A2a')
Declare @XML xml
Set @XML = (Select * from @YourTable for XML RAW)
Select ID = r.value('@id','int') --<<'@id' Should be YOUR PK
,Item = Attr.value('local-name(.)','varchar(100)')
,Value = Attr.value('.','varchar(max)')
From @XML.nodes('/row') as A(r)
Cross Apply A.r.nodes('./@*[local-name(.)!="id"]') as B(Attr) --<<'id' Should be YOUR PK
--CROSS APPLY A.r.nodes('./@*') AS B(Attr)
返回(空值通常是你的PK)
ID Item Value
NULL Year 2016
NULL ProductID 1
NULL ProductName Test1
NULL Option1 A1
NULL Option2 A1a
NULL Option3 A3
NULL Year 2015
NULL ProductID 1
NULL ProductName Test1
NULL Option1 A1
NULL Option2 A2
NULL Option3 A2a
答案 1 :(得分:1)
我会将表规范化为单个表,每个选项包含行数:
select pm.year, pm.productid, pm.productname, v.option, v.optionvalue
from productmapping pm cross apply
(values ('option1', option1), ('option2', option2), . . .
) v(option, optionvalue);
然而,我正在努力把它放到两张桌子里。我可以想象给选项提供ID(但不是选项/值对)。