2010-12-05 156 views
1

產生XML數據行我已經在SQL Server 2008中的3個表內的設置如下:SQL Server從聯接的SELECT語句

EMPLOYEE表

empid(PK) 
1 
2 

加入到EMPLOYEEATTRIBUTES

dataId(PK) | empId(FK) | attributeid | attributeVal 
10 | 1 | A1 | somevalue1 
20 | 1 | A2 | somevalue2 
30 | 2 | A1 | somevalue3 
40 | 2 | A3 | somevalue4 

加入ATTRIBUTES

attributeid | attributeName 
A1 | attribute1 
A2 | attribute2 
A3 | attribute3 

我需要出去獲得XML數據轉換成以下格式

<rows> 
    <row empid="1"> 
    <attribute1>somevalue1</attribute1> 
    <attribute2>somevalue2</attribute1> 
    </row> 
    <row empid="2"> 
    <attribute1>somevalue3</attribute1> 
    <attribute3>somevalue4</attribute1> 
    </row> 
</rows> 

有誰知道如何可以做到這一點?

+0

如果您發佈的代碼或XML,** **請突出顯示文本的那些行編輯器,然後單擊編輯器工具欄上的「代碼」按鈕(101 010)以良好地格式化和語法突出顯示它! – 2010-12-05 20:54:31

+0

xml以上應爲:

  somevalue1 somevalue2   somevalue3 somevalue4  
任何人都知道如何做到這一點? – Puc 2010-12-05 20:56:42

+0

請看我的評論 - 編輯器中有一個「代碼」按鈕 - 使用它! – 2010-12-05 21:04:47

回答

1

你可以關閉 - 但你不能得到你想要的輸出100%。

使用此查詢:

SELECT 
    EmpID AS '@empid', 
    (
     SELECT 
      a.AttributeName AS '@name', 
      ea.AttributeVal 
     FROM dbo.EmployeeAttributes ea 
     INNER JOIN dbo.Attributes a ON ea.AttributeId = a.AttributeId 
     WHERE ea.EmpID = e.EmpID 
     FOR XML PATH ('attribute'), TYPE 
    ) 
FROM dbo.Employee e 
FOR XML PATH('row'), ROOT('rows') 

你會得到這樣的輸出:

<rows> 
    <row empid="1"> 
    <attribute name="Attribute1"> 
     <AttributeVal>SomeValue1</AttributeVal> 
    </attribute> 
    <attribute name="attribute2"> 
     <AttributeVal>SomeValue2</AttributeVal> 
    </attribute> 
    </row> 
    <row empid="2"> 
    <attribute name="Attribute1"> 
     <AttributeVal>SomeValue3</AttributeVal> 
    </attribute> 
    <attribute name="attribute3"> 
     <AttributeVal>SomeValue4</AttributeVal> 
    </attribute> 
    </row> 
</rows> 

什麼你不能做的是使內部的XML節點具有匹配屬性名稱標籤名稱 - 你必須使用一些固定標籤名稱(如我的示例中的<attribute>),然後將從表中檢索到的值作爲這些XML標籤上的屬性(例如我的示例中的name=屬性)或作爲XML元素值。

據我所知,目前還沒有辦法使用AttributeValue作爲XML標籤名稱....

0

這裏有一個答案,但轉動命令限制你,你必須知道的名字您的屬性提前。隨着一點點的調整,你也許可以做到這一點動態(嘗試在SQL Server 2005中尋找動態的支點):

DECLARE @Employee TABLE (empid INT) 
DECLARE @EA TABLE 
    (
     dataid INT 
    , empid INT 
    , attributeid CHAR(2) 
    , AttributeVal VARCHAR(100) 
    ) 
DECLARE @Attributes TABLE 
    (
     AttributeID CHAR(2) 
    , AttributeName VARCHAR(100) 
    ) 

INSERT INTO @Employee 
VALUES (1), 
     (2) 

INSERT INTO @EA 
     (dataid, empid, attributeid, AttributeVal) 
VALUES (10, 1, 'A1', 'somevalue1') 
    , (20, 1, 'A2', 'somevalue2') 
    , (30, 2, 'A1', 'somevalue3') 
    , (40, 2, 'A3', 'somevalue4') 

INSERT INTO @Attributes 
     (AttributeID, AttributeName) 
VALUES ('A1', 'attribute1') 
     , 
     ('A2', 'attribute2') 
     , 
     ('A3', 'attribute3') 

SELECT empID as '@empid' 
     , attribute1 
     , attribute2 
     , attribute3 
     , attribute4 
FROM (SELECT e.empid 
        , a.AttributeName 
        , ea.AttributeVal 
      FROM  @Employee e 
        JOIN @EA ea ON e.empid = ea.empid 
        JOIN @Attributes a ON ea.attributeid = a.attributeid 
     ) ps PIVOT 
(MIN(AttributeVal) FOR AttributeName IN ([attribute1], [attribute2], [attribute3], [attribute4])) AS pvt  
FOR XML PATH('row'), ROOT('rows') 
2

如果你想跳過所有的血淋淋的細節,只是看到一個答案,查看SQL查詢在這篇文章的底部。

這裏的主要挑戰是各種SQL Server選項無法生成所需輸出中規定的動態元素名稱。因此,我的第一個答案是考慮簡單地返回一個常規的SQL結果集並讓客戶端生成XML。這是一個非常簡單的流媒體轉換。但是,這可能不適合您,所以我們繼續讓SQL Server生成XML。

我的第二個想法是使用SQL Server的內置的XQuery功能來進行轉換,這樣的:

/* WARNING: the following SQL does not work */ 
SELECT 
    CAST((SELECT * FROM data FOR XML RAW) AS XML) 
    .query(' 
     <rows> 
     { 
      for $empId in distinct-values(/row/@empId) 
      return 
      <row empid="{$empId}"> 
      { 
       for $attr in /row[@empId = $empId] 
       return 
       attribute { "attribute" } { $attr/@attributeValue } 
      } 
      </row> 
     } 
     </rows> 
    ') 

唉,這是行不通的。 SQL Server的抱怨:

Msg 9315, Level 16, State 1, Line 25 
XQuery [query()]: Only constant expressions are supported for the name expression 
of computed element and attribute constructors. 

顯然,XQuery實現從相同的限制受到的FOR XML功能。所以,我的第二個回答是建議在客戶端生成XML :)但是,如果你堅持從SQL生成XML,然後繫好安全帶...

總體戰略是放棄SQL Server的SQL生成的本地設施。相反,我們將使用字符串連接來構建XML文檔。如果這種做法是進攻,你現在可以停止閱讀:)

讓我們先從產生的樣本數據集一起玩:在所提供的例子

SELECT NULL AS empId INTO employee WHERE 1=0 
UNION SELECT 1 
UNION SELECT 2 

SELECT NULL AS dataId, NULL AS empId, NULL AS attributeId, NULL AS attributeVal INTO employeeAttributes WHERE 1=0 
UNION SELECT 10, 1, 'A1', 'someValue1' 
UNION SELECT 20, 1, 'A2', 'someValue2' 
UNION SELECT 30, 2, 'A1', 'someValue3' 
UNION SELECT 40, 2, 'A3', 'someValue4 & <>!' 

SELECT NULL AS attributeId, NULL AS attributeName INTO attributes WHERE 1=0 
UNION SELECT 'A1', 'attribute1' 
UNION SELECT 'A2', 'attribute2' 
UNION SELECT 'A3', 'attribute3' 

請注意,我已經改變了最後一個屬性的值包括一些XML不友好的字符。

現在,把一個基本的SQL查詢來執行必要的連接:

SELECT 
    e.empId 
, a.attributeName 
, ea.attributeVal 
FROM employee AS e 
INNER JOIN employeeAttributes AS ea 
    ON ea.empId = e.empId 
INNER JOIN attributes AS a 
    ON a.attributeId = ea.attributeId 

這給出了這樣的結果:

empId attributeName attributeVal 
1  attribute1  someValue1 
1  attribute2  someValue2 
2  attribute1  someValue3 
2  attribute3  someValue4 & <>! 

在最後一個屬性的那些奇怪的字符,將會給我們帶來麻煩。讓我們改變查詢來轉義它們。

; WITH 
    cruftyData AS (
    SELECT 
     e.empId 
     , a.attributeName 
     , (SELECT ea.attributeVal AS x FOR XML RAW) AS attributeValXml 
     FROM employee AS e 
     INNER JOIN employeeAttributes AS ea 
     ON ea.empId = e.empId 
     INNER JOIN attributes AS a 
     ON a.attributeId = ea.attributeId 
    ) 
, data AS (
    SELECT 
     empId 
    , attributeName 
    , SUBSTRING(attributeValXml, 9, LEN(attributeValXml)-11) AS attributeVal 
    FROM cruftyData 
) 
SELECT * FROM data 

與結果:

empId attributeName attributeValXml 
1  attribute1  someValue1 
1  attribute2  someValue2 
2  attribute1  someValue3 
2  attribute3  someValue4 &amp; &lt;&gt;! 

這確保了屬性值現在可以在XML文檔中安全地使用。那屬性名稱呢? XML屬性名稱的規則比元素內容的規則更嚴格。我們將假定屬性名稱是有效的XML標識符。如果不是這樣,那麼需要設計一些方案將數據庫中的名稱轉換爲有效的XML名稱。這是作爲練習留給讀者:)

下一個挑戰是確保屬性爲每個員工分組在一起,並且我們可以知道我們何時處於組中的第一個或最後一個值。以下是更新的查詢:

; WITH 
    cruftyData AS (
    SELECT 
     e.empId 
     , a.attributeName 
     , (SELECT ea.attributeVal AS x FOR XML RAW) AS attributeValXml 
     FROM employee AS e 
     INNER JOIN employeeAttributes AS ea 
     ON ea.empId = e.empId 
     INNER JOIN attributes AS a 
     ON a.attributeId = ea.attributeId 
    ) 
, data AS (
    SELECT 
     empId 
    , attributeName 
    , SUBSTRING(attributeValXml, 9, LEN(attributeValXml)-11) AS attributeVal 
    , ROW_NUMBER() OVER (PARTITION BY empId ORDER BY attributeName DESC) AS down 
    , ROW_NUMBER() OVER (PARTITION BY empId ORDER BY attributeName) AS up 
    FROM cruftyData 
) 
SELECT * FROM data ORDER BY 1, 2 

唯一的變化是將列添加到結果集:

empId attributeName attributeVal    down up 
1  attribute1  someValue1     2  1 
1  attribute2  someValue2     1  2 
2  attribute1  someValue3     2  1 
2  attribute3  someValue4 &amp; &lt;&gt;! 1  2 

我們現在能夠確定的第一個屬性爲僱員因爲以上將是。最後的屬性可以使用下的列以類似的方式識別。

有了這一切,我們現在可以執行使用字符串連接來構建XML結果的討厭業務了。

; WITH 
    cruftyData AS (
    SELECT 
     e.empId 
     , a.attributeName 
     , (SELECT ea.attributeVal AS x FOR XML RAW) AS attributeValXml 
     FROM employee AS e 
     INNER JOIN employeeAttributes AS ea 
     ON ea.empId = e.empId 
     INNER JOIN attributes AS a 
     ON a.attributeId = ea.attributeId 
    ) 
, data AS (
    SELECT 
     empId 
    , attributeName 
    , SUBSTRING(attributeValXml, 9, LEN(attributeValXml)-11) AS attributeVal 
    , ROW_NUMBER() OVER (PARTITION BY empId ORDER BY attributeName DESC) AS down 
    , ROW_NUMBER() OVER (PARTITION BY empId ORDER BY attributeName) AS up 
    FROM cruftyData 
) 
, xmlData AS (
    SELECT 
    empId 
    , up 
    , CASE WHEN up <> 1 THEN '' ELSE '<row id="' + CAST (empId AS NVARCHAR) + '">' END AS xml1 
    , '<' + attributeName + '>' + attributeVal + '</' + attributeName + '>' AS xml2 
    , CASE WHEN down <> 1 THEN '' ELSE '</row>' END AS xml3 
    FROM data 
) 
SELECT xml1, xml2, xml3 
--SELECT @result = @result + 'wombat' + xmlString 
FROM xmlData 
ORDER BY empId, up 

與結果:

xml1   xml2             xml3 
<row id="1"> <attribute1>someValue1</attribute1>   
       <attribute2>someValue2</attribute2>     </row> 
<row id="2"> <attribute1>someValue3</attribute1>   
       <attribute3>someValue4 &amp; &lt;&gt;!</attribute3> </row> 

剩下的是連接所有的行在一起,並添加根標籤。由於T-SQL沒有(還)有字符串連接集合函數,因此我們將使用一個變量作爲累加器。這裏是最終的查詢,在其所有的榮耀哈克

DECLARE @result AS NVARCHAR(MAX) 
SELECT @result = '<rows>' 

; WITH 
    cruftyData AS (
    SELECT 
     e.empId 
     , a.attributeName 
     , (SELECT ea.attributeVal AS x FOR XML RAW) AS attributeValXml 
     FROM employee AS e 
     INNER JOIN employeeAttributes AS ea 
     ON ea.empId = e.empId 
     INNER JOIN attributes AS a 
     ON a.attributeId = ea.attributeId 
    ) 
, data AS (
    SELECT 
     empId 
    , attributeName 
    , SUBSTRING(attributeValXml, 9, LEN(attributeValXml)-11) AS attributeVal 
    , ROW_NUMBER() OVER (PARTITION BY empId ORDER BY attributeName DESC) AS down 
    , ROW_NUMBER() OVER (PARTITION BY empId ORDER BY attributeName) AS up 
    FROM cruftyData 
) 
, xmlData AS (
    SELECT 
    empId 
    , up 
    , CASE WHEN up <> 1 THEN '' ELSE '<row id="' + CAST (empId AS NVARCHAR) + '">' END AS xml1 
    , '<' + attributeName + '>' + attributeVal + '</' + attributeName + '>' AS xml2 
    , CASE WHEN down <> 1 THEN '' ELSE '</row>' END AS xml3 
    FROM data 
) 
SELECT @result = @result + xml1 + xml2 + xml3 
FROM xmlData 
ORDER BY empId, up 

SELECT @result = @result + '</rows>' 
SELECT @result 

的XML在@result變量結束。您可以檢查它是否使用格式良好的XML:

SELECT CAST(@result AS XML) 

最終的XML看起來是這樣的:

<rows><row id="1"><attribute1>someValue1</attribute1><attribute2>someValue2</attribute2></row><row id="2"><attribute1>someValue3</attribute1><attribute3>someValue4 &amp; &lt;&gt;!</attribute3></row></rows>