2012-06-18 76 views
3

我有兩個與Id列相關的表(使用表變量進行說明,您可以直接在management studio中運行這些表)。將sql表中的xml字符串轉換爲動態列

第一個表中的項目具有一些標準的列集,第二個表具有一些用於相同記錄的擴展參數數據。我將擴展集存儲爲xml,因爲它在所有方面都是動態的(不同的每個產品或新的值被添加等)。

我可以連接這兩個表格並將列列表展平,如下面的示例中所示。但我的查詢需要事先定義動態列。我想這是真正的動態,因爲如果我要在@extended表中添加一個新列,它應該會自動作爲輸出列列表中的新列出現。

基本上,附加列的列表應該由該記錄的xml確定。列名應該是xml標記,值應該是每個id的xml標記的值。

任何指針? (並且可以在快速過與100k左右或者更多的記錄在每個表)

declare @standard table 
(
    Id INT, 
    Column1 varchar(10), 
    Column2 varchar(10), 
    Column3 varchar(10) 
) 

declare @extended table 
(
    Id INT, 
    column1 xml 
) 

insert into @standard values (1,'11', '12', '13') 
insert into @standard values (2,'21', '22', '23') 

insert into @extended values (1,'<FieldSet><Field><id>1</id><column4>1x</column4><column5>4x</column5></Field></FieldSet>') 
insert into @extended values (2,'<FieldSet><Field><id>2</id><column4>2x</column4><column5>5x</column5></Field></FieldSet>') 

select s.column1, s.column2, 

( 
    SELECT Item2.value('(column4)[1]', 'varchar(50)') 
    FROM 
    e.column1.nodes('/FieldSet') AS T(Item) 
    CROSS APPLY e.column1.nodes('/FieldSet/Field') AS T2(Item2) 

) column4, 

( 
    SELECT Item2.value('(column5)[1]', 'varchar(50)') 
    FROM 
    e.column1.nodes('/FieldSet') AS T(Item) 
    CROSS APPLY e.column1.nodes('/FieldSet/Field') AS T2(Item2) 

) column5 

from @extended e 
join @standard s on s.Id = e.Id 
+0

我看到你說的話。但是這是一個巨大系統中非常小的部分,你需要在特定的實體上存儲一些屬性。這些屬性(名稱值對)爲每個產品的實體動態添加,並且在產品之間有所不同。這個實體是我的例子中的標準表,擴展的是屬性。接下來有什麼其他的選擇來處理這種情況。我可能有一個有點複雜的3或4表組,並處理這個,但這似乎更簡單。 –

+0

當然,馬克刪除了他的評論,所以我的評論是一個孤兒。無論如何,這個「http://en.wikipedia.org/wiki/Entity-attribute-value_model」是我所追求的。 XML確實是一種替代方案,但現在,我將使用常規EAV表。 –

回答

2

首先,你可以簡化您的當前查詢了一下。

select s.column1, 
     s.column2, 
     e.column1.value('(/FieldSet/Field/column4)[1]', 'varchar(50)') as column4, 
     e.column1.value('(/FieldSet/Field/column5)[1]', 'varchar(50)') as column5 
from extended as e 
    join standard as s 
    on s.Id = e.Id 

做你想做的事情並不容易或快速。您需要獲取XML中所有名稱/值對的列表。

select T1.X.value('.', 'int') as Id, 
     T2.X.value('local-name(.)', 'sysname') as Name, 
     T2.X.value('.', 'varchar(10)') as Value 
from extended as e 
    cross apply e.column1.nodes('/FieldSet/Field/id') as T1(X) 
    cross apply e.column1.nodes('/FieldSet/Field/*[position() > 1]') as T2(X) 

使用,在樞軸查詢和加盟standard

select S.column1, 
     S.column2, 
     P.column4, 
     P.column5 
from standard as s 
    inner join 
     (
     select id, P.column4, P.column5 
     from (
      select T1.X.value('.', 'int') as Id, 
        T2.X.value('local-name(.)', 'sysname') as Name, 
        T2.X.value('.', 'varchar(10)') as Value 
      from extended as e 
      cross apply e.column1.nodes('/FieldSet/Field/id') as T1(X) 
      cross apply e.column1.nodes('/FieldSet/Field/*[position() > 1]') as T2(X) 
      ) as e 
     pivot (min(Value) for Name in (column4, column5)) P 
    ) P 
    on S.Id = P.Id 

要使用動態數量的列返回,您需要動態構建此數據透視查詢。
將名稱/值對存儲在臨時表中,使用該表找出您需要的列並構建查詢。

create table #ext 
(
    Id int, 
    Name sysname, 
    Value varchar(10), 
    primary key(Id, Name) 
) 

insert into #ext(Id, Name, Value) 
select T1.X.value('.', 'int') as Id, 
     T2.X.value('local-name(.)', 'sysname') as Name, 
     T2.X.value('.', 'varchar(10)') as Value 
from extended as e 
    cross apply e.column1.nodes('/FieldSet/Field/id') as T1(X) 
    cross apply e.column1.nodes('/FieldSet/Field/*[position() > 1]') as T2(X) 

declare @SQL nvarchar(max) 
set @SQL = 
'select S.column1, 
     S.column2, 
     [COLLIST] 
from standard as s 
    inner join 
     (
     select id, [COLLIST] 
     from #ext as e 
     pivot (min(Value) for Name in ([COLLIST])) P 
    ) P 
    on S.Id = P.Id' 

declare @ColList nvarchar(max) 

set @ColList = 
    (select ','+Name 
    from #ext 
    group by Name 
    for xml path(''), type).value('.', 'nvarchar(max)') 

set @SQL = replace(@SQL, '[COLLIST]', stuff(@ColList, 1, 1, '')) 

exec (@SQL) 

drop table #ext 
+0

謝謝你的回答。我可以用EAV表來代替。但是你回答我的問題。 –

0

我希望它會幫助你

SELECT @ COUNT_XML = 0

    SELECT @COUNT_XML=(SELECT @xxxxx_GROUP_ID.value('count(/NewDataSet/position/ID)', 'INT')) 

        IF(@COUNT_XML > 0)    

         BEGIN 
          IF OBJECT_ID('tempdb..#TBL_TEMPOSITION') IS NOT NULL 
          DROP TABLE #TBL_TEMPOSITION 

          CREATE TABLE #TBL_TEMPOSITION (ID NUMERIC(18,0)) 
          INSERT INTO #TBL_TEMPOSITION (ID)  
          SELECT XMLxxxxGroup.PositionGPItem.value('.','NUMERIC(18,0)') 
          FROM @xxxxx_GROUP_ID.nodes('/NewDataSet/position/ID') AS XMLPositionGroup(PositionGPItem) 
          SELECT @emp_cond [email protected]_cond+ N' AND CM.STATIC_EMP_INFO.POSITION_ID IN (SELECT ID FROM #TBL_TEMPOSITION) '   
         END