2016-01-13 31 views
2

我有一個很直接的xml文檔。唯一的區別是元素可以改變。有一次,我可能有:來自sql中的openxml的動態列和記錄

<data><PersonalInfo> 
<Person><FirstName>Bob</FirstName><LastName>Smith</LastName></Person> 
<Person><FirstName>John</FirstName><LastName>Doe</LastName></Person> 
</PersonalInfo></data> 

下一次我可能有:

<data><AddressInfo> 
<Address><City>Cleveland</City><State>OH</State></Address> 
<Address><City>Chicago</City><State>IL</State></Address> 
</AddressInfo></data> 

我還想寫上XML文檔我在拿到產生一個動態表的select語句取決於此時此刻。

例:對於第一個:

First Name  Last Name 
------------------------ 
Bob    Smith 
John   Doe 
Etc... 

對於第二一個

City  State 
----------------------- 
Cleveland OH 
Chicago  IL 
Etc... 

的2個實施例不以任何方式關聯(鮑勃不是來自克利夫蘭,等...)

我只是想使用相同的代碼來生成這兩個表...取決於xml文檔。當然,唯一的區別將是節點引用:

Example 1: data/PersonalInfo/Person* 
Example 2: data/AddressInfo/Address* 

我不想在xml文檔結構中組合或更改任何內容。 他們是他們進來的。我怎樣才能引用每個創建上面的兩個不同的表 - 每個xml文件進來將在一個單獨的運行存儲過程。但它將是相同的存儲過程。任何援助非常感謝,提前致謝!

+0

我忘了提及 - 我不想在產生表的代碼中硬編碼字段名稱。如果可能的話,我想讓代碼知道xml文檔中的字段名稱並創建它! –

回答

3

試試這樣說:

CREATE PROCEDURE dbo.TransformPlainXML(@InputXml XML) 
AS 
BEGIN 

DECLARE @PivotColumns NVARCHAR(MAX); 

WITH DistinctElementNames AS 
(
    SELECT DISTINCT '[' + Element.value('fn:local-name(.)','varchar(max)') + ']' AS ElementName 
    FROM @InputXml.nodes('/data/*/*/*') As One(Element) 
) 
SELECT @PivotColumns = STUFF(
(
    SELECT ',' + ElementName 
    FROM DistinctElementNames 
    FOR XML PATH('') 
),1,1,''); 

DECLARE @cmd NVARCHAR(MAX)= 
'WITH Lines AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowIndex 
      ,Line.query(''.'') AS OneLine 
    FROM @xml.nodes(''/data/*/*'') As One(Line) 
) 
SELECT p.* 
FROM 
(
    SELECT RowIndex 
      ,Element.value(''.[1]'',''varchar(max)'') AS ElementValue 
      ,Element.value(''fn:local-name(.)'',''varchar(max)'') AS ElementName 
    FROM Lines 
    CROSS APPLY OneLine.nodes(''./*/*'') AS The(Element) 
) AS tbl 
PIVOT 
(
    MIN(ElementValue) FOR ElementName IN(' + @PivotColumns + ') 
) AS p 
'; 

EXECUTE sp_executesql @cmd,N'@xml XML',@[email protected]; 
END 
GO 

和測試它像這樣

declare @xml1 XML= 
'<data> 
    <PersonalInfo> 
    <Person> 
     <FirstName>Bob</FirstName> 
     <LastName>Smith</LastName> 
    </Person> 
    <Person> 
     <FirstName>John</FirstName> 
     <LastName>Doe</LastName> 
    </Person> 
    </PersonalInfo> 
</data>'; 


EXEC TransformPlainXML @xml1; 

declare @xml2 XML= 
'<data> 
    <AddressInfo> 
    <Address> 
     <City>Cleveland</City> 
     <State>OH</State> 
    </Address> 
    <Address> 
     <City>Chicago</City> 
     <State>IL</State> 
    </Address> 
    </AddressInfo> 
</data>'; 
EXEC TransformPlainXML @xml2; 

兩個結果:

RowIndex FirstName LastName 
1   Bob   Smith 
2   John  Doe 

而且

RowIndex City  State 
1   Cleveland OH 
2   Chicago  IL 
+0

我幾乎感到興奮,並準備好使用它,當我得到以下錯誤:「PIVOT附近的語法不正確。您可能需要將當前數據庫的兼容級別設置爲更高的值以啓用此功能。 ALTER DATABASE的SET COMPATIBILITY_LEVEL選項「。我們正在使用2008 R2,可悲的是,我無法改變兼容性,除非我更瞭解這一點。但它看起來像很棒的代碼! ;-( –

+2

@DanielRaymondPatfield,'PIVOT'已引入[已與SQL Server 2005](https://technet.microsoft.com/en-us/library/ms177410(v = sql.105).aspx)...請注意,「使用2008 R2」並不意味着你的實際數據庫完全以2008R2運行,假設SS2008R2中的BAK文件打開了一箇舊的SS2000數據庫,它仍然會知道它是SS2000下的SS2000 。1)在主數據庫的上下文中運行我的腳本。 2)看看數據庫選項,並找到應該是90或更高的實際兼容性級別。 – Shnugo

+0

@DanielRaymondPatfield,這個問題仍然存在嗎?我的解決方案不適合你嗎?如果你的問題解決了,你應該通過接受一個答案來關閉它(或者寫你自己的答案)。我的意見是:是的,我的答案解決了這個問題,因爲你沒有對版本提出任何限制。無論如何,SQL2008R2應該足夠了。這個平臺是靠聲望點生活的,你只用了兩次這個機會。請開始對您的投票權非常慷慨!投入好的貢獻和糟糕的下降。請通過接受來解決您的問題。 Thx – Shnugo

1

您可以創建一個列映射表,存儲的「映射模板」任何金額...(缺乏一個更好的描述)

讓您的新表中有值:

TYPE  ORDER   COLUMNNAME 
Person  1    FirstName 
Person  2    LastName 
Address  1    City 
Address  2    State 

然後使用此表來加載實際的xml節點名稱並使用循環從XML中收集值。

這是可行的?你有權限在SQL中創建和讀取此表嗎?

+0

我很欣賞這個答覆,但是我想擺脫其他任何物體。我自從使用字段名稱/值生成一個臨時表,但我寧願創建一個表,其中列名是動態創建的水平(根據我的上述示例)而不是垂直。 –

+1

使用另一個XML文件來控制你的映射怎麼樣?您可以在XML文件中創建數據表(與上面的答案相同)...將其加載到內存中並使用它來創建具有任意列數的XML輸出 - 在XML文件中指定 – Grantly

+0

另一個很好的答案 - 我們做這樣的事情很多。但我希望有一段解析XML代碼可以做到這一點。只是不知道它是什麼。只需查看標籤元素並將其放置爲表格即可。我現在可以將它們放在一個列中有「節點名稱」和「節點值」的表格中 - 但我更喜歡動態創建的水平表格。 –