XML को संशोधित करें: तत्वों में विशेषताएँ


11

मेरे पास एक XMLकॉलम है जिसमें समान संरचना वाला डेटा है:

<Root>
    <Elements>
        <Element Code="1" Value="aaa"></Element>
        <Element Code="2" Value="bbb"></Element>
        <Element Code="3" Value="ccc"></Element>
    </Elements>
</Root>

मैं प्रत्येक Valueतत्व को एक तत्व में बदलने के लिए SQL सर्वर का उपयोग करके डेटा को कैसे संशोधित कर सकता हूं ?

<Root>
    <Elements>
        <Element Code="1">
            <Value>aaa</Value>
        </Element>
        <Element Code="2">
            <Value>bbb</Value>
        </Element>
        <Element Code="3">
            <Value>ccc</Value>
        </Element>
    </Elements>
</Root>

अपडेट करें:

मेरा XML इस तरह दिखता है:

<Root attr1="val1" attr2="val2">
    <Elements>
        <Element Code="1" Value="aaa" ExtraData="extra" />
        <Element Code="2" Value="bbb" ExtraData="extra" />
        <Element Code="3" Value="ccc" ExtraData="extra" />
        <Element Code="4" Value="" ExtraData="extra" />
        <Element Code="5" ExtraData="extra" />
    </Elements>
    <ExtraData>
       <!-- Some XML is here -->
    </ExtraData>
</Root>

मैं केवल Valueविशेषता को स्थानांतरित करना और अन्य सभी विशेषताओं और तत्वों को संरक्षित करना चाहूंगा ।


आप पहली जगह में ऐसा क्यों करना चाहते हैं? जब तक आप <Value>प्रत्येक पर कई तत्व रखने की योजना नहीं बनाते हैं, तब तक मैं इसके किसी भी लाभ के बारे में नहीं सोच सकता <Element>। यदि नहीं, तो विशेषता को एक तत्व में ले जाना अधिक फूला हुआ, और संभवतः कम कुशल, एक्सएमएल के लिए बनाता है।
सोलोमन रटज़की

@srutzky, यह एक refactoring का एक हिस्सा है। दूसरा चरण <Value>तत्व के अंदर या इसके बजाय जटिल डेटा संग्रहीत कर रहा है।
वोज्टेक्ट

जवाबों:


13

आप XML को निकाल सकते हैं और XQuery का उपयोग करके इसे फिर से बना सकते हैं।

declare @X xml = '
<Root attr1="val1" attr2="val2">
    <Elements>
        <Element Code="1" Value="aaa" ExtraData="extra" />
        <Element Code="2" Value="" ExtraData="extra" />
        <Element Code="3" ExtraData="extra" />
    </Elements>
    <ExtraData>
       <!-- Some XML is here -->
    </ExtraData>
</Root>';

select @X.query('
  (: Create element Root :)
  element Root 
    {
      (: Add all attributes from Root to Root :)
      /Root/@*, 
      (: create element Elements under Root :)
      element Elements 
        {
          (: For each Element element in /Root/Elements :)
          for $e in /Root/Elements/Element
          return 
            (: Add element Element :)
            element Element 
              {
                (: Add all attributes except Value to Element :)
                $e/@*[local-name() != "Value"], 

                (: Check if Attribute Value exist :)
                if (data($e/@Value) != "")
                then
                  (: Create a Value element under Element :)
                  element Value 
                  {
                    (: Add attribute Value as data to the element Element :)
                    data($e/@Value)
                  }
                else () (: Empty element :)
              } 
          },
      (: Add all childelements to Root except the Elements element :)
      /Root/*[local-name() != "Elements"]
    }');

परिणाम:

<Root attr1="val1" attr2="val2">
  <Elements>
    <Element Code="1" ExtraData="extra">
      <Value>aaa</Value>
    </Element>
    <Element Code="2" ExtraData="extra" />
    <Element Code="3" ExtraData="extra" />
  </Elements>
  <ExtraData>
    <!-- Some XML is here -->
  </ExtraData>
</Root>

यदि क्वेरी के Elementsतहत पहला तत्व पहले और सभी तत्वों Rootको जोड़ने के बाद सभी तत्वों को जोड़ने के लिए संशोधित करने की आवश्यकता नहीं है ।ElementsElements


5

आप XML डेटाटाइप (जैसे संशोधित ) और xml को संशोधित करने के लिए कुछ XQuery के तरीकों का भी उपयोग कर सकते हैं , जैसे

DECLARE @x XML = '<Root attr1="val1" attr2="val2">
    <Elements>
        <Element Code="1" Value="aaa" ExtraData="extra" />
        <Element Code="2" Value="bbb" ExtraData="extra" />
        <Element Code="3" Value="ccc" ExtraData="extra" />
    </Elements>
    <ExtraData>
       <!-- Some XML is here -->
    </ExtraData>
</Root>'


SELECT 'before' s, DATALENGTH(@x) dl, @x x

-- Add 'Value' element to each Element which doesn't already have one
DECLARE @i INT = 0

WHILE @x.exist('Root/Elements/Element[not(Value)]') = 1
BEGIN

    SET @x.modify( 'insert element Value {data(Root/Elements/Element[not(Value)]/@Value)[1]} into (Root/Elements/Element[not(Value)])[1]' )

    SET @i += 1

    IF @i > 99 BEGIN RAISERROR( 'Too many loops...', 16, 1 ) BREAK END

END

-- Now delete all Value attributes
SET @x.modify('delete Root/Elements/Element/@Value' )

SELECT 'after' s, DATALENGTH(@x) dl, @x x

यह विधि XML के बड़े टुकड़ों पर अच्छी तरह से लागू नहीं होती है, लेकिन यह आपको XML के थोक प्रतिस्थापन की तुलना में बेहतर हो सकती है।

यदि आपका XML किसी तालिका में संग्रहीत है, तो आप इस विधि को आसानी से अनुकूलित कर सकते हैं। अनुभव से फिर मैं एक लाख पंक्ति तालिका के खिलाफ एक भी अद्यतन चलाने की सिफारिश नहीं करूंगा। यदि आपकी तालिका बड़ी है, तो इसके माध्यम से एक कर्सर चलाने या अपडेट को बैचने पर विचार करें। यहाँ तकनीक है:

DECLARE @t TABLE ( rowId INT IDENTITY PRIMARY KEY, yourXML XML )

INSERT INTO @t ( yourXML )
SELECT '<Root attr1="val1" attr2="val2">
    <Elements>
        <Element Code="1" Value="aaa" ExtraData="extra" />
        <Element Code="2" Value="bbb" ExtraData="extra" />
        <Element Code="3" Value="ccc" ExtraData="extra" />
    </Elements>
    <ExtraData>
       <!-- Some XML is here -->
    </ExtraData>
</Root>'

INSERT INTO @t ( yourXML )
SELECT '<Root attr1="val1" attr2="val2">
    <Elements>
        <Element Code="21" Value="uuu" ExtraData="extra" />
        <Element Code="22" Value="vvv" ExtraData="extra" />
        <Element Code="23" Value="www" ExtraData="extra" />
        <Element Code="24" Value="xxx" ExtraData="extra" />
        <Element Code="25" Value="yyy" ExtraData="extra" />
        <Element Code="26" Value="zzz" ExtraData="extra" />
    </Elements>
    <ExtraData>
       <!-- Some XML is here -->
    </ExtraData>
</Root>'


SELECT 'before' s, DATALENGTH(yourXML) dl, yourXML
FROM @t 

-- Add 'Value' element to each Element which doesn't already have one
DECLARE @i INT = 0

WHILE EXISTS ( SELECT * FROM @t WHERE yourXML.exist('Root/Elements/Element[not(Value)]') = 1 )
BEGIN

    UPDATE @t
    SET yourXML.modify( 'insert element Value {data(Root/Elements/Element[not(Value)]/@Value)[1]} into (Root/Elements/Element[not(Value)])[1]' )

    SET @i += 1

    IF @i > 99 BEGIN RAISERROR( 'Too many loops...', 16, 1 ) BREAK END

END

-- Now delete all Value attributes
UPDATE @t
SET yourXML.modify('delete Root/Elements/Element/@Value' )

SELECT 'after' s, DATALENGTH(yourXML) dl, yourXML
FROM @t 

4

अपडेट करें:

मैंने कोड को अपडेट किया है, साथ ही इनपुट और आउटपुट एक्सएमएल में उदाहरण क्वेरी में नवीनतम आवश्यकता को प्रतिबिंबित करने के लिए नीचे, @ मिकेल के ठीक उत्तर पर एक टिप्पणी में कहा गया है , जो है:

@Value रिक्त है या मौजूद नहीं है, तो मान तत्व बनाने के लिए नहीं

हालांकि एक एकल अभिव्यक्ति इस नई भिन्नता को सही ढंग से मेल कर सकती है, लेकिन <Value/>एकल पास में खाली तत्व को छोड़ने का एक तरीका नहीं दिखता है क्योंकि प्रतिस्थापन स्ट्रिंग में सशर्त तर्क की अनुमति नहीं है। इसलिए, मैंने इसे 2 भाग संशोधन के लिए अनुकूलित किया है: एक पास गैर-खाली @Valueविशेषताओं को प्राप्त करने के लिए और एक पास खाली @Valueविशेषताओं को प्राप्त करने के लिए । वैसे भी तत्व नहीं <Element>होने के @Valueकारण विशेषता को याद रखने की कोई आवश्यकता नहीं थी <Value>


एक विकल्प XML को एक नियमित स्ट्रिंग के रूप में मानने और इसे एक पैटर्न के आधार पर बदलने का है। यह नियमित एक्सप्रेशन (विशेष रूप से "बदलें" फ़ंक्शन) का उपयोग करके आसानी से पूरा किया जाता है, जिसे SQLCLR कोड के माध्यम से उपलब्ध कराया जा सकता है।

नीचे दिया गया उदाहरण SQL # लाइब्रेरी से RegEx_Replace स्केलर UDF का उपयोग करता है (जो मैं इसका लेखक हूं, लेकिन यह RegEx फ़ंक्शन नि: शुल्क संस्करण में उपलब्ध है, साथ ही कई अन्य):

DECLARE @SomeXml XML;
SET @SomeXml = N'<Root attr1="val1" attr2="val2">
    <Elements>
        <Element Code="1" Value="aaa" ExtraData="extra1" />
        <Element Code="22" Value="bbb" ExtraData="extra2" />
        <Element Code="333" Value="ccc" ExtraData="extra3" />
        <Element Code="4444" Value="" ExtraData="extra4" />
        <Element Code="55555" ExtraData="extra5" />
    </Elements>
    <ExtraData>
       <Something Val="1">qwerty A</Something>
       <Something Val="2">qwerty B</Something>
    </ExtraData>
</Root>';

DECLARE @TempStringOfXml NVARCHAR(MAX),
        @Expression NVARCHAR(4000),
        @Replacement NVARCHAR(4000);


SET @TempStringOfXml = CONVERT(NVARCHAR(MAX), @SomeXml);
PRINT N'Original: ' + @TempStringOfXml;

---

SET @Expression =
              N'(<Element Code="[^"]+")\s+Value="([^"]+)"\s+(ExtraData="[^"]+")\s*/>';
SET @Replacement = N'$1 $3><Value>$2</Value></Element>';

SELECT @TempStringOfXml = SQL#.RegEx_Replace(@TempStringOfXml, @Expression,
                                             @Replacement, -1, 1, '');

PRINT '-------------------------------------';
PRINT N'Phase 1:  ' + @TempStringOfXml; -- transform Elements with a non-empty @Value

---

SET @Expression = N'(<Element Code="[^"]+")\s+Value=""\s+(ExtraData="[^"]+")\s*/>';
SET @Replacement = N'$1 $2 />';

SELECT @TempStringOfXml = SQL#.RegEx_Replace(@TempStringOfXml, @Expression,
                                             @Replacement, -1, 1, '');

PRINT '-------------------------------------';
PRINT N'Phase 2:  ' + @TempStringOfXml; -- transform Elements with an empty @Value

SELECT CONVERT(XML, @TempStringOfXml); -- prove that this is valid XML

PRINTबयान सिर्फ "संदेश" टैब में तुलना करने में आसानी साइड-बाई-साइड के लिए बनाने के लिए कर रहे हैं। परिणामी आउटपुट है (मैंने मूल XML को थोड़ा संशोधित किया ताकि यह स्पष्ट हो सके कि केवल वांछित भागों को छुआ गया था और कुछ नहीं):

Original: <Root attr1="val1" attr2="val2"><Elements><Element Code="1" Value="aaa" ExtraData="extra1"/><Element Code="22" Value="bbb" ExtraData="extra2"/><Element Code="333" Value="ccc" ExtraData="extra3"/><Element Code="4444" Value="" ExtraData="extra4"/><Element Code="55555" ExtraData="extra5"/></Elements><ExtraData><Something Val="1">qwerty A</Something><Something Val="2">qwerty B</Something></ExtraData></Root>
-------------------------------------
Phase 1:  <Root attr1="val1" attr2="val2"><Elements><Element Code="1" ExtraData="extra1"><Value>aaa</Value></Element><Element Code="22" ExtraData="extra2"><Value>bbb</Value></Element><Element Code="333" ExtraData="extra3"><Value>ccc</Value></Element><Element Code="4444" Value="" ExtraData="extra4"/><Element Code="55555" ExtraData="extra5"/></Elements><ExtraData><Something Val="1">qwerty A</Something><Something Val="2">qwerty B</Something></ExtraData></Root>
-------------------------------------
Phase 2:  <Root attr1="val1" attr2="val2"><Elements><Element Code="1" ExtraData="extra1"><Value>aaa</Value></Element><Element Code="22" ExtraData="extra2"><Value>bbb</Value></Element><Element Code="333" ExtraData="extra3"><Value>ccc</Value></Element><Element Code="4444" ExtraData="extra4" /><Element Code="55555" ExtraData="extra5"/></Elements><ExtraData><Something Val="1">qwerty A</Something><Something Val="2">qwerty B</Something></ExtraData></Root>

यदि आप किसी तालिका में किसी फ़ील्ड को अपडेट करना चाहते हैं, तो आप निम्नलिखित होने के लिए उपरोक्त को अनुकूलित कर सकते हैं:

DECLARE @NonEmptyValueExpression NVARCHAR(4000),
        @NonEmptyValueReplacement NVARCHAR(4000),
        @EmptyValueExpression NVARCHAR(4000),
        @EmptyValueReplacement NVARCHAR(4000);

SET @NonEmptyValueExpression =
                   N'(<Element Code="[^"]+")\s+Value="([^"]+)"\s+(ExtraData="[^"]+")\s*/>';
SET @NonEmptyValueReplacement = N'$1 $3><Value>$2</Value></Element>';

SET @EmptyValueExpression =
                   N'(<Element Code="[^"]+")\s+Value=""\s+(ExtraData="[^"]+")\s*/>';
SET @EmptyValueReplacement = N'$1 $2 />';

UPDATE tbl
SET    XmlField = SQL#.RegEx_Replace4k(
                                     SQL#.RegEx_Replace4k(
                                                     CONVERT(NVARCHAR(4000), tbl.XmlField),
                                                        @NonEmptyValueExpression,
                                                        @NonEmptyValueReplacement,
                                                        -1, 1, ''),
                                     @EmptyValueExpression,
                                     @EmptyValueReplacement,
                                     -1, 1, '')
FROM   SchemaName.TableName tbl
WHERE  tbl.XmlField.exist('Root/Elements/Element/@Value') = 1;

आपका समाधान अच्छा लग रहा है और यह मददगार था, लेकिन आईकैन सीएलआर का उपयोग करता है।
वोज्टेक्ट

@Wojteq धन्यवाद। यह विकल्पों के लिए अच्छा है, है ना? जिज्ञासा से बाहर, आप SQLCLR का उपयोग करने में सक्षम क्यों नहीं हैं?
सोलोमन रटज़की 15

यह हमारी वास्तुकला के कारण है। हमें मल्टी-टेनेंसी वेब एप्लिकेशन मिला है। हर किरायेदार का अपना डेटाबेस होता है। हम किसी अन्य 'मूविंग पार्ट' को नहीं जोड़ना चाहते जो तैनाती की प्रक्रिया के दौरान विफल हो सके। कोड-ओनली / वेबएप-ओनली एप्रोच का उपयोग करना हमारे लिए बहुत अधिक अनुरक्षणीय है।
वोज्टेक्ट

1

SQL सर्वर के बाहर इसे करने के बेहतर तरीके हैं। हालांकि, यह करने का एक तरीका है।

आपका डेटा:

declare @xml xml = N'<Root>
    <Elements>
        <Element Code="1" Value="aaa"></Element>
        <Element Code="2" Value="bbb"></Element>
        <Element Code="3" Value="ccc"></Element>
    </Elements>
</Root>';

प्रश्न:

With xml as (
    Select 
        Code = x.e.value('(@Code)', 'varchar(10)')
        , Value = x.e.value('(@Value)', 'varchar(10)')
    From @xml.nodes('/Root//Elements/Element') as x(e)
)
Select * From (
    Select code
        , (
        Select value
        From xml x1 where x1.Code = Element.Code
        For xml path(''), elements, type
    )
    From xml Element
    For xml auto, type
) as Root(Elements)
for xml auto, elements;

Xml CTE आपके xml वैरिएबल को एक टेबल में बदलता है।

मुख्य चयन फिर सीटीई को एक्सएमएल में बदल देता है।

आउटपुट:

<Root>
  <Elements>
    <Element code="1">
      <value>aaa</value>
    </Element>
    <Element code="2">
      <value>bbb</value>
    </Element>
    <Element code="3">
      <value>ccc</value>
    </Element>
  </Elements>
</Root>

इसका उपयोग करके भी किया जा सकता है For XML Explicit


आपकी मदद के लिए धन्यवाद, हालांकि मैंने अपना प्रश्न अपडेट कर लिया है - मेरा मामला जटिल है। मैं प्रदर्शन के कारण SQL सर्वर का उपयोग करके अपने XML को अपडेट करना चाहता हूं। मुझे ऐसी मेजें मिली हैं जिनमें सैकड़ों हजारों रिकॉर्ड हैं। अन्य विकल्प इसे लोड करना है, एएसआर एमवीसी एप्लिकेशन के अंदर इसे डीरेल करना और क्रमबद्ध करना है।
वोज्टेक्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.