2008-10-10 65 views
14

我從具有使用T-SQL的XML列的表中進行選擇。我想選擇某種類型的節點併爲每個節點創建一行。選擇XML節點作爲行

例如,假設我從表中選擇。此表有一個用於地址的XML列。 XML被格式化類似以下內容:

<address> 
    <street>Street 1</street> 
    <city>City 1</city> 
    <state>State 1</state> 
    <zipcode>Zip Code 1</zipcode> 
</address> 
<address> 
    <street>Street 2</street> 
    <city>City 2</city> 
    <state>State 2</state> 
    <zipcode>Zip Code 2</zipcode> 
</address> 

我怎樣才能得到的結果是這樣的:

名稱                                     國家

喬·貝克     西雅圖            WA

喬·貝克     塔科馬          WA

弗雷德 - 瓊斯   溫哥華

回答

31

這裏是您的解決方案:

/* TEST TABLE */ 
DECLARE @PEOPLE AS TABLE ([Name] VARCHAR(20), [Address] XML) 
INSERT INTO @PEOPLE SELECT 
    'Joel', 
    '<address> 
     <street>Street 1</street> 
     <city>City 1</city> 
     <state>State 1</state> 
     <zipcode>Zip Code 1</zipcode> 
    </address> 
    <address> 
     <street>Street 2</street> 
     <city>City 2</city> 
     <state>State 2</state> 
     <zipcode>Zip Code 2</zipcode> 
    </address>' 
UNION ALL SELECT 
    'Kim', 
    '<address> 
     <street>Street 3</street> 
     <city>City 3</city> 
     <state>State 3</state> 
     <zipcode>Zip Code 3</zipcode> 
    </address>' 

SELECT * FROM @PEOPLE 

-- BUILD XML 
DECLARE @x XML 
SELECT @x = 
(SELECT 
     [Name] 
    , [Address].query(' 
      for $a in //address 
      return <address 
       street="{$a/street}" 
       city="{$a/city}" 
       state="{$a/state}" 
       zipcode="{$a/zipcode}" 
      /> 
     ') 
    FROM @PEOPLE AS people 
    FOR XML AUTO 
) 

-- RESULTS 
SELECT [Name] = T.Item.value('../@Name', 'varchar(20)'), 
     street = T.Item.value('@street' , 'varchar(20)'), 
     city  = T.Item.value('@city' , 'varchar(20)'), 
     state  = T.Item.value('@state' , 'varchar(20)'), 
     zipcode = T.Item.value('@zipcode', 'varchar(20)') 
FROM @x.nodes('//people/address') AS T(Item) 

/* OUTPUT*/ 

Name | street | city | state | zipcode 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
Joel | Street 1 | City 1 | State 1 | Zip Code 1 
Joel | Street 2 | City 2 | State 2 | Zip Code 2 
Kim | Street 3 | City 3 | State 3 | Zip Code 3 
-4

如果你可以用它 ,LINQ的API,方便XML:

var addresses = dataContext.People.Addresses 
    .Elements("address") 
     .Select(address => new { 
      street = address.Element("street").Value, 
      city = address.Element("city").Value, 
      state = address.Element("state").Value, 
      zipcode = address.Element("zipcode").Value, 
     }); 
+3

他的工作在T-SQL,不C# – FlySwat 2008-10-10 17:53:10

+0

我知道,但linq呈現給t-sql。 – Wyatt 2008-10-10 17:59:36

1

這裏是我如何做到這一點一般:

我通過一個調用,如

 


DECLARE @xmlEntityList xml 
SET @xmlEntityList = 
' 
<ArbitrarilyNamedXmlListElement> 
       <ArbitrarilyNamedXmlItemElement><SomeVeryImportantInteger>1</SomeVeryImportantInteger></ArbitrarilyNamedXmlItemElement> 
       <ArbitrarilyNamedXmlItemElement><SomeVeryImportantInteger>2</SomeVeryImportantInteger></ArbitrarilyNamedXmlItemElement> 
       <ArbitrarilyNamedXmlItemElement><SomeVeryImportantInteger>3</SomeVeryImportantInteger></ArbitrarilyNamedXmlItemElement> 
</ArbitrarilyNamedXmlListElement> 
' 

    DECLARE @tblEntityList TABLE(
     SomeVeryImportantInteger int 
    ) 

    INSERT @tblEntityList(SomeVeryImportantInteger) 
    SELECT 
     XmlItem.query('//SomeVeryImportantInteger[1]').value('.','int') as SomeVeryImportantInteger 
    FROM 
     [dbo].[tvfShredGetOneColumnedTableOfXmlItems] (@xmlEntityList) 


 

通過利用標量值函數

 

/* Example Inputs */ 
/* 
DECLARE @xmlListFormat xml 
SET  @xmlListFormat = 
      ' 
      <ArbitrarilyNamedXmlListElement> 
       <ArbitrarilyNamedXmlItemElement>004421UB7</ArbitrarilyNamedXmlItemElement> 
       <ArbitrarilyNamedXmlItemElement>59020UH24</ArbitrarilyNamedXmlItemElement> 
       <ArbitrarilyNamedXmlItemElement>542514NA8</ArbitrarilyNamedXmlItemElement> 
      </ArbitrarilyNamedXmlListElement> 
      ' 
declare @tblResults TABLE 
(
    XmlItem xml 
) 

*/ 

-- ============================================= 
-- Author:  6eorge Jetson 
-- Create date: 01/02/3003 
-- Description: Shreds a list of XML items conforming to 
--    the expected generic @xmlListFormat 
-- ============================================= 
CREATE FUNCTION [dbo].[tvfShredGetOneColumnedTableOfXmlItems] 
(
    -- Add the parameters for the function here 
    @xmlListFormat xml 
) 
RETURNS 
@tblResults TABLE 
(
    -- Add the column definitions for the TABLE variable here 
    XmlItem xml 
) 
AS 
BEGIN 

    -- Fill the table variable with the rows for your result set 
    INSERT @tblResults 
    SELECT 
     tblShredded.colXmlItem.query('.') as XmlItem 
    FROM 
     @xmlListFormat.nodes('/child::*/child::*') as tblShredded(colXmlItem) 

    RETURN 
END 

--SELECT * FROM @tblResults 

 
0

在這種情況下是到其他人那裏尋找一個「通用」溶液是有用的,我創建,可以採取XML片段如上述和CLR過程「分解」成一個表格的結果集,沒有你提供有關列的名稱或類型的任何其他信息,或自定義您的通話以任何方式給定XML片段:

http://architectshack.com/ClrXmlShredder.ashx

當然也有一些restri ctions(xml必須像本示例一樣是「表格」,第一行需要包含所有將被支持的元素/列,等等) - 但我確實希望它比內置可用內容早幾步。

0

這裏是一個替代的解決方案:

;with cte as 
(
    select id, name, addresses, addresses.value('count(/address/city)','int') cnt 
    from @demo 
) 
, cte2 as 
(
    select id, name, addresses, addresses.value('((/address/city)[sql:column("cnt")])[1]','nvarchar(256)') city, cnt-1 idx 
    from cte 
    where cnt > 0 

    union all 

    select cte.id, cte.name, cte.addresses, cte.addresses.value('((/address/city)[sql:column("cte2.idx")])[1]','nvarchar(256)'), cte2.idx-1 
    from cte2 
    inner join cte on cte.id = cte2.id and cte2.idx > 0 
) 
select id, name, city 
from cte2 
order by id, city 

FYI:我已經張貼在代碼審查的網站在這裏這個SQL的另一個版本:https://codereview.stackexchange.com/questions/108805/select-field-in-an-xml-column-where-both-xml-and-table-contain-multiple-matches