如何非规范化此 SQL 服务器 table?
How to denormalize this SQL Server table?
我有一个非常宽的 table 非规范化(如果你可以这么说的话),选项列转到 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'
我 运行 遇到的问题是如何通过查询将宽 table 转换为两个 table 结构,因为它有很多列和行,我无法规范化所有这些都是手动的。
是的,我也理解理想情况下,第二个 table 需要进一步规范化,以将 Option1...Option3 保持在单独的 table 中,并将 Option1..A1 映射保持在单独的 table 但这是一个开始...
希望这个简单的例子能够阐明以下事实
- Option1...100 列需要在单独的 table
中标准化
- 值映射的选项列每年都在变化
有什么想法吗?
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%'
Returns
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
EDIT
现在,如果你想要一个 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)
Returns(空值通常是您的 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
我会将 table 规范化为单个 table,每个选项包含行:
select pm.year, pm.productid, pm.productname, v.option, v.optionvalue
from productmapping pm cross apply
(values ('option1', option1), ('option2', option2), . . .
) v(option, optionvalue);
不过,我正在努力将其放入两个 table 中。我可以想象为选项提供 ID(但不是 option/value 对)。
我有一个非常宽的 table 非规范化(如果你可以这么说的话),选项列转到 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'
我 运行 遇到的问题是如何通过查询将宽 table 转换为两个 table 结构,因为它有很多列和行,我无法规范化所有这些都是手动的。
是的,我也理解理想情况下,第二个 table 需要进一步规范化,以将 Option1...Option3 保持在单独的 table 中,并将 Option1..A1 映射保持在单独的 table 但这是一个开始...
希望这个简单的例子能够阐明以下事实
- Option1...100 列需要在单独的 table 中标准化
- 值映射的选项列每年都在变化
有什么想法吗?
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%'
Returns
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
EDIT
现在,如果你想要一个 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)
Returns(空值通常是您的 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
我会将 table 规范化为单个 table,每个选项包含行:
select pm.year, pm.productid, pm.productname, v.option, v.optionvalue
from productmapping pm cross apply
(values ('option1', option1), ('option2', option2), . . .
) v(option, optionvalue);
不过,我正在努力将其放入两个 table 中。我可以想象为选项提供 ID(但不是 option/value 对)。