2009-07-15 25 views
15

में XML दस्तावेज़ में नोड ऑर्डर ढूँढना मुझे XML दस्तावेज़ में नोड्स का ऑर्डर कैसे मिल सकता है?SQL सर्वर

क्या मैं इस तरह के एक दस्तावेज है:

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

और मैं एक मेज

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

एक्सएमएल से मानों का क्रम संरक्षण की तरह परिभाषित में इस बात को पाने के लिए कोशिश कर रहा हूँ दस्तावेज़ (उन्हें उनके कोड द्वारा आदेश नहीं दिया जा सकता है)। मैं कहना है

SELECT code 
FROM values 
WHERE parent_code = 121 
ORDER BY ord 

सक्षम होना चाहते हैं और परिणाम, निर्धारणात्मक, होना चाहिए

code 
1211 
1212 

मैं

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 

की कोशिश की है लेकिन यह position() समारोह को स्वीकार नहीं करता (' position() 'का उपयोग केवल भविष्यवाणी या XPath चयनकर्ता के भीतर किया जा सकता है)।

मुझे लगता है कि यह किसी भी तरह से संभव है, लेकिन कैसे?

+0

आप नोड्स के परिमित गहराई है? यदि नहीं, तो यह दर्द होगा। और पुष्टि करने के लिए: आप कोड पर भरोसा नहीं कर सकते? – gbn

+0

... और आप एक्सएमएल से क्या आउटपुट चाहते हैं? – gbn

+0

मैंने अधिक जानकारी प्रदान करने के लिए प्रश्न अपडेट किया। और नहीं, एक अनंत गहराई है। – erikkallen

उत्तर

31

आप भाई नोड्स प्रत्येक नोड पूर्ववर्ती की संख्या की गणना position() समारोह का अनुकरण कर सकते हैं:

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 

यह कैसे काम करता:

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) 

यहाँ परिणाम सेट है

  • for $i in . खंड $i नामक चर को परिभाषित करता है जिसमें वर्तमान नोड (.) शामिल है। यह मूल रूप से XQuery की एक्सएसएलटी की तरह current() फ़ंक्शन की कमी के आसपास काम करने के लिए एक हैक है।
  • ../* अभिव्यक्ति वर्तमान नोड के सभी भाई बहनों (माता-पिता के बच्चे) का चयन करती है।
  • [. << $i] भविष्यवाणी() से पहले (<<) से पहले भाई बहनों की सूची फ़िल्टर करता है।
  • हम count() पूर्व भाई बहनों की संख्या और फिर स्थिति पाने के लिए 1 जोड़ें।

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

    उदाहरण के लिए: इस तरह प्रथम नोड (जो कोई पूर्ववर्ती भाई बहन) की 1.

+2

मैंने इस कोड को एक बड़ी एक्सएमएल फाइल पर इस्तेमाल किया और क्योंकि 'i $ i में। वापसी की गिनती (../* [। << $ i]) + 1' भाग हमेशा के लिए हर नोड से पहले सभी "भाई" नोड्स को पार करता है (हम इसे घर जाने के दौरान काम पर चलने देते हैं, यह अगले दिन दुर्घटनाग्रस्त हो गया था)। तो सावधान रहें कि इस कोड में ओ (एन^2) दक्षता है। – funkwurm

2

this दस्तावेज़ के अनुसार और यह connect entry यह संभव नहीं है, लेकिन कनेक्ट एंट्री में दो वर्कअराउंड हैं।

मैं इस तरह यह कार्य करें:

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

हर बार जब मैं यह देखने का प्रयास करता हूं कि ऐसा करने का कोई अच्छा तरीका है तो मैं हमेशा रोना पसंद करता हूं। यह एकमात्र तरीका है जिसे मैंने पाया है (वास्तव में, मैं एक संख्या तालिका का उपयोग करता हूं, लेकिन एक ही बदसूरत हैक) - यह एक सर्वर के लिए बिल्कुल दयनीय बहाना है जो "एक्सएमएल का समर्थन करता है" और सरल श्रेय बनाता है और इसे जितना जटिल होना चाहिए उससे कहीं अधिक जटिल । –

3

जवाब erikkallen द्वारा पूरी तरह से सही है।

हालांकि, यदि मूल दस्तावेज़/स्कीमा संशोधित किया जा सकता है, तो विकल्प एक विशेषता में स्थिति/अनुक्रमणिका को संग्रहीत करना है। मैं दोनों दृष्टिकोणों का मिश्रण उपयोग करता हूं, इस पर निर्भर करता हूं कि एक्सएमएल का "उत्प्रेरक" कौन है और इस पर किए जाने वाले प्रश्नों के प्रकार। दिन के अंत में मैं एसक्यूएल सर्वर में संभवतः "गूंगा भंडारण" को छोड़कर एक्सएमएल का अधिकतर उपयोग करता हूं और आमतौर पर खुश हूं जब मैं सामान्यीकृत टेबल के लिए इसे (एक्सएमएल) डंप कर सकता हूं।

"एंटरप्राइज़-ग्रेड" उत्पादों की अनियमित सीमाओं से मुबारक व्यवहार - आश्चर्य कभी खत्म नहीं होता है।

+0

+1 आपके "एक्सएम के अधिकांश उपयोग के लिए"। यह वास्तव में बेकार होता है जब आपको नोड्स को बहुत कम अपडेट करना होता है। –

4

की स्थिति आप एक्सएमएल तो जैसे एक x.nodes() समारोह से वापस लौटे की स्थिति प्राप्त कर सकते हैं असाइन किया गया है :

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

धन्यवाद @ बेन, मेरे पास नया सोल्यूशन row_number() है (ऑर्डर (नल का चयन करें)) –

+0

@nick_n_a, अच्छा। – Ben

+0

@nick_n_a, आपके विचार के आधार पर अपडेट किया गया। – Ben

2

एसक्यूएल सर्वर के row_number() वास्तव में एक xml-नोड्स द्वारा ऑर्डर करने के लिए स्तंभ स्वीकार करता है। आप ऐसा कर सकते हैं recursive CTE के साथ संयुक्त: एक तरफ एक के रूप में

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 

, तो आप ऐसा कर सकते हैं और यह आप क्या उम्मीद करते हैं करेंगे:

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

क्यों, अगर यह काम करता है, आप के बिना सीधे order by x.ml नहीं हो सकता है।

0

मैं @Ben और द्वारा उत्तर देखें ... मिल नई sollution

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

रूप

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)