2016-08-11 81 views
2

我有一個非常寬的表非規範化的(如果你能說)這樣的,選項欄去100+如何對這個SQL Server表進行非規範化處理?

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' 

我遇到的問題是如何通過查詢將該寬錶轉換爲兩個表結構,因爲它有很多列和行,並且我無法手動規範化所有這些。

是的,我也明白了理想的第二個表需要進一步保持選項1 ... 2選項在單獨的表,並在一個單獨的表Option1..A1映射的標準,但它是一個開始......

希望簡單的例子揭示了以下事實

  1. 選項1 ... 100列需要在一個單獨的表被歸
  2. 選項列值映射的光每年都在變化

有什麼想法?

+0

您是否考慮過使用文檔存儲數據庫而不是關係型(表)數據庫?可擴展列是傳統RDBMS的一個弱點。 – Dai

+0

太多的現有流程依賴於這些核心表......我正在嘗試將我的替代方案作爲概念驗證來擺脫我們正在進行的所有動態查詢 – SQLSeeker

+0

爲什麼您需要兩個表格解決方案?這是什麼讓你無法用標準化的單表獲得?只需在第一個表中添加'OptionValue',你就可以將一切表格中的所有東西都歸一化。 –

回答

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 

編輯

現在,如果你想直Normaliz通貨膨脹

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 
+0

這絕對是輝煌!工程...但是一些OptionX列有NULL,年顯示爲NULL爲他們? – SQLSeeker

+0

@SQLSeeker讓我再試一次吧 –

+0

@SQLSeeker剛剛在2017年添加了虛擬行,在Option3中爲null。我在2012年看到兩行(每組)。也許你可以提供樣本 –

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(但不是選項/值對)。