2009-07-15 25 views
15

Làm cách nào để tìm thứ tự các nút trong tài liệu XML?Tìm thứ tự nút trong tài liệu XML trong SQL Server

gì tôi có là một tài liệu như thế này:

<value code="1"> 
    <value code="11"> 
     <value code="111"/> 
    </value> 
    <value code="12"> 
     <value code="121"> 
      <value code="1211"/> 
      <value code="1212"/> 
     </value> 
    </value> 
</value> 

và tôi đang cố gắng để có được điều này vào một bảng được định nghĩa như

CREATE TABLE values(
    code int, 
    parent_code int, 
    ord int 
) 

Bảo tồn thứ tự của các giá trị từ XML tài liệu (chúng không thể được sắp xếp theo mã của chúng). Tôi muốn để có thể nói

SELECT code 
FROM values 
WHERE parent_code = 121 
ORDER BY ord 

và kết quả nên, tất định, hãy

code 
1211 
1212 

Tôi đã thử

SELECT 
    value.value('@code', 'varchar(20)') code, 
    value.value('../@code', 'varchar(20)') parent, 
    value.value('position()', 'int') 
FROM @xml.nodes('/root//value') n(value) 
ORDER BY code desc 

Nhưng nó không chấp nhận position() chức năng (' position() 'chỉ có thể được sử dụng trong bộ chọn biến vị ngữ hoặc XPath).

Tôi đoán có thể có cách nào đó, nhưng làm cách nào?

+0

Bạn có chiều sâu hữu hạn các nút? Nếu không, nó sẽ là một nỗi đau. Và để xác nhận: bạn không thể dựa vào mã? – gbn

+0

... và bạn muốn sản lượng nào từ xml? – gbn

+0

Tôi đã cập nhật câu hỏi để cung cấp thêm thông tin. Và không, có chiều sâu vô hạn. – erikkallen

Trả lời

31

Bạn có thể bắt chước các position() chức năng bằng cách đếm số lượng anh chị em nút trước mỗi nút:

SELECT 
    code = value.value('@code', 'int'), 
    parent_code = value.value('../@code', 'int'), 
    ord = value.value('for $i in . return count(../*[. << $i]) + 1', 'int') 
FROM @Xml.nodes('//value') AS T(value) 

Dưới đây là tập hợp kết quả:

code parent_code ord 
---- ----------- --- 
1  NULL   1 
11  1   1 
111 11   1 
12  1   2 
121 12   1 
1211 121   1 
1212 121   2 

Cách hoạt động:

  • Điều khoản for $i in . xác định biến có tên là $i có chứa nút hiện tại (.). Đây là cơ bản một hack để làm việc xung quanh thiếu XQuery của một chức năng giống như XSLT current().
  • Biểu thức ../* chọn tất cả anh chị em (con của cha mẹ) của nút hiện tại.
  • Vị từ [. << $i] lọc danh sách anh chị em đối với những người đứng trước (<<) nút hiện tại ($i).
  • Chúng tôi count() số lượng anh chị em trước và sau đó thêm 1 để nhận vị trí. Bằng cách đó nút đầu tiên (trong đó có không anh chị em trước) được gán một vị trí 1.
+2

Tôi đã sử dụng mã này trên một tệp XML khá lớn và vì 'cho $ i in. return count (../* [. << $ i]) + 1' phần đi qua tất cả các nút "anh chị em" trước mỗi nút này mất mãi mãi (chúng tôi để cho nó chạy tại nơi làm việc trong khi về nhà, nó đã bị rơi vào ngày hôm sau). Vì vậy, hãy cảnh báo rằng mã này có hiệu quả O (n^2). – funkwurm

2

Theo tài liệu this và điều này connect entry không thể, nhưng mục Kết nối chứa hai cách giải quyết.

tôi làm điều đó như thế này:

WITH n(i) AS (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9), 
    o(i) AS (SELECT n3.i * 100 + n2.i * 10 + n1.i FROM n n1, n n2, n n3) 
SELECT v.value('@code', 'varchar(20)') AS code, 
     v.value('../@code', 'varchar(20)') AS parent, 
     o.i AS ord 
    FROM o 
CROSS APPLY @xml.nodes('/root//value[sql:column("o.i")]') x(v) 
ORDER BY o.i 
+0

Mỗi lần tôi cố gắng xem liệu có cách nào tốt để thực hiện điều này tôi luôn luôn cảm thấy như đang khóc. Đó là cách duy nhất tôi tìm thấy (thực ra, tôi sử dụng một bảng số, nhưng cùng một cách xấu xí) - đó là một lý do hoàn toàn thảm hại đối với một máy chủ "hỗ trợ XML" và làm cho việc băm nhỏ đơn giản và truy cập phức tạp hơn nhiều so với nó . –

3

Câu trả lời bởi erikkallen là hoàn toàn chính xác.

Tuy nhiên, nếu tài liệu/lược đồ gốc có thể được sửa đổi, cách khác là lưu trữ vị trí/chỉ mục trong thuộc tính. Tôi sử dụng kết hợp cả hai cách tiếp cận, tùy thuộc vào "người khởi tạo" của XML là gì và loại truy vấn nào cần được thực hiện trên đó. Vào cuối ngày, tôi hầu hết sử dụng XML ngoại trừ khả năng "lưu trữ câm" trong SQL Server và thường hạnh phúc khi tôi có thể đổ nó (XML) cho các bảng được chuẩn hóa.

Vui lòng giải quyết những hạn chế không đề cập đến của sản phẩm "cấp doanh nghiệp" - kỳ quan không bao giờ kết thúc.

+0

+1 cho "hầu hết việc sử dụng XML của bạn". Nó thực sự hút khi bạn phải chọn các nút ít cập nhật chúng hơn. –

4

Bạn có thể có được vị trí của xml trả về bởi một hàm x.nodes() như vậy:

row_number() over (order by (select 0)) 

Ví dụ :

DECLARE @x XML 
SET @x = '<a><b><c>abc1</c><c>def1</c></b><b><c>abc2</c><c>def2</c></b></a>' 

SELECT 
    b.query('.'), 
    row_number() over (partition by 0 order by (select 0)) 
FROM 
    @x.nodes('/a/b') x(b) 
+1

Cảm ơn @Ben, tôi có sollution new row_number() over (order by (select null)) –

+0

@nick_n_a, tốt đẹp. – Ben

+0

@nick_n_a, được cập nhật dựa trên ý tưởng của bạn. – Ben

2

SQL server row_number() thực sự chấp nhận một cột xml-nút để tự do. Kết hợp với một recursive CTE bạn có thể làm điều này:

declare @Xml xml = 
'<value code="1"> 
    <value code="11"> 
     <value code="111"/> 
    </value> 
    <value code="12"> 
     <value code="121"> 
      <value code="1211"/> 
      <value code="1212"/> 
     </value> 
    </value> 
</value>' 

;with recur as (
    select 
     ordr  = row_number() over(order by x.ml), 
     parent_code = cast('' as varchar(255)), 
     code  = x.ml.value('@code', 'varchar(255)'), 
     children = x.ml.query('./value') 
    from @Xml.nodes('value') x(ml) 
    union all 
    select 
     ordr  = row_number() over(order by x.ml), 
     parent_code = recur.code, 
     code  = x.ml.value('@code', 'varchar(255)'), 
     children = x.ml.query('./value') 
    from recur 
    cross apply recur.children.nodes('value') x(ml) 
) 
select * 
from recur 
where parent_code = '121' 
order by ordr 

Là một sang một bên, bạn có thể làm điều này và nó sẽ làm những gì bạn mong đợi:

select x.ml.query('.') 
from @Xml.nodes('value/value')x(ml) 
order by row_number() over (order by x.ml) 

Tại sao, nếu làm việc này, bạn không thể chỉ order by x.ml trực tiếp mà không có row_number() over nằm ngoài tôi.

0

Tôi thấy câu trả lời bằng @Ben và ... lấy sollution mới

row_number() over (order by (select null)) 

như

SELECT value.value('@code', 'varchar(20)') code, 
    value.value('../@code', 'varchar(20)') parent, 
    row_number() over (order by (select null)) 
    FROM @xml.nodes('/root//value') n(value) 
Các vấn đề liên quan