2008-10-10 29 views
14

Tôi đang chọn từ một bảng có cột XML sử dụng T-SQL. Tôi muốn chọn một loại nút nhất định và có một hàng được tạo cho mỗi nút. Ví dụ:Chọn các nút XML dưới dạng hàng

Ví dụ: giả sử tôi đang chọn từ bảng mọi người. Bảng này có cột XML cho địa chỉ. XML được formated tương tự như sau:

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

Làm thế nào tôi có thể nhận được kết quả như thế này:

Tên                   Thành phố              .210   Nhà nước

Joe Baker       Seattle             WA

Joe Baker       Tacoma           WA

Fred Jones     Vancouver   BC

Trả lời

31

Đây là giải pháp của bạn:

/* 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 
+3

Tìm kiếm một lúc cho đến khi tôi tìm thấy ví dụ tuyệt vời này. Kudos leoinfo! –

-4

Nếu bạn có thể sử dụng nó, các api LINQ là thuận tiện cho 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

Ông làm việc trong T-SQL, không phải C# – FlySwat

+0

tôi biết , nhưng LINQ ám tới t-sql. – Wyatt

+1

Tôi đoán đây là một thủ tục được lưu trữ. – FlySwat

1

Đây là cách tôi làm điều đó quát:

Tôi đã băm nhỏ nguồn XML thông qua một cuộc gọi như

 


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) 


 

bằng cách sử dụng vô hướng có giá trị chức năng

 

/* 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

Trong trường hợp này rất hữu ích cho bất cứ ai khác ngoài kia đang tìm kiếm một giải pháp "chung chung", tôi tạo ra một thủ tục CLR có thể mất một mảnh Xml như trên và "xóa sạch" nó thành một resultset bảng, mà không cần bạn cung cấp bất kỳ thông tin bổ sung về tên hoặc các loại của các cột, hoặc tùy biến cuộc gọi của bạn trong bất kỳ cách nào cho đoạn Xml đưa ra:

http://architectshack.com/ClrXmlShredder.ashx

có tất nhiên một số restri ction (xml phải là "bảng" trong tự nhiên như mẫu này, hàng đầu tiên cần chứa tất cả các phần tử/cột sẽ được hỗ trợ, v.v) - nhưng tôi hy vọng đó là một vài bước trước những gì có sẵn.

0

Dưới đây là một giải pháp thay thế:

;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: Tôi đã đăng một phiên bản của SQL này trên trang web đánh giá mã ở đây: https://codereview.stackexchange.com/questions/108805/select-field-in-an-xml-column-where-both-xml-and-table-contain-multiple-matches

Các vấn đề liên quan