এক্সএমএল সংশোধন করুন: উপাদানগুলিতে গুণাবলী


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বৈশিষ্ট্যকে একটি উপাদানে পরিবর্তন করতে আমি কীভাবে এসকিউএল সার্ভার ব্যবহার করে ডেটা পরিবর্তন করতে পারি ?

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

হালনাগাদ:

আমার এক্সএমএল দেখতে আরও বেশি লাগে:

<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>। যদি তা না হয়, তবে একটি উপাদানের সাথে অ্যাট্রিবিউটটি সরিয়ে নেওয়া আরও প্রস্ফুটিত এবং সম্ভবত কম দক্ষ, এক্সএমএল তৈরি করে।
সলোমন রুটজকি

@ শ্রুতজকি, এটি রিফ্যাক্টরিংয়ের একটি অংশ। দ্বিতীয় পদক্ষেপটি <Value>উপাদান বা তার পরিবর্তে জটিল ডেটা সংরক্ষণ করে is
ওয়াজটেক

উত্তর:


13

আপনি এক্সএমএলটি ছেঁকে ফেলতে পারেন এবং এক্সকিউয়ারি ব্যবহার করে এটি পুনরায় বিল্ড করতে পারেন।

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আগে Elementsএবং পরে সমস্ত উপাদান যুক্ত করার জন্য সংশোধন করা দরকার Elements


5

আপনি এক্সএমএল ডেটাটাইপ (যেমন, সংশোধন ) এবং কিছু এক্সকিউয়ের পদ্ধতি ব্যবহার করতে পারেন এক্সএমএল সংশোধন করতে, যেমন

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

এই পদ্ধতিটি এক্সএমএলের বৃহত টুকরাগুলিতে ভাল স্কেল করার প্রবণতা রাখে না তবে এটি আপনাকে এক্সএমএলের পাইকারি প্রতিস্থাপনের চেয়ে ভাল মানায়।

যদি আপনার এক্সএমএল কোনও টেবিলে সঞ্চয় করা থাকে তবে আপনি সহজেই এই পদ্ধতিটি মানিয়ে নিতে পারেন। আবার অভিজ্ঞতা থেকে আমি মিলিয়ন রো সারণির বিপরীতে একটি আপডেট আপডেট করার পরামর্শ দেব না। যদি আপনার টেবিলটি বড় হয় তবে এটির মাধ্যমে একটি কার্সার চালানো বা অন্যথায় আপডেটগুলি ব্যাচিংয়ের বিষয়টি বিবেচনা করুন। কৌশলটি এখানে:

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/> একক পাসে উপাদানটি হচ্ছে না কারণ প্রতিস্থাপনের স্ট্রিংয়ে শর্তাধীন যুক্তি অনুমোদিত নয়। সুতরাং, আমি এটি একটি 2 অংশ সংশোধন হিসাবে মানিয়ে নিয়েছি: খালি খালি @Valueবৈশিষ্ট্য পেতে একটি পাস এবং খালি বৈশিষ্ট্য পেতে একটি পাস @Value। বৈশিষ্ট্যটি <Element>অনুপস্থিত হ্যান্ডেল করার দরকার ছিল না @Valueকারণ <Value>ইচ্ছেটি কোনওভাবেই উপাদানটি না পাওয়া।


একটি বিকল্প হ'ল এক্সএমএলকে একটি নিয়মিত স্ট্রিং হিসাবে বিবেচনা করা এবং কোনও প্যাটার্নের ভিত্তিতে এটিকে রূপান্তর করা। এটি সহজেই নিয়মিত এক্সপ্রেশন (বিশেষত "প্রতিস্থাপন" ফাংশন) ব্যবহার করে সম্পন্ন হয় যা এসকিউএলসিআর কোডের মাধ্যমে উপলব্ধ করা যায়।

নীচের উদাহরণটি ব্যবহার করে এসকিউএল # লাইব্রেরি থেকে RegEx_Replace স্কেলার ইউডিএফ (যার লেখক আমি, তবে এই 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বিবৃতি সেখানে শুধু "বার্তা" ট্যাবে সহজ তুলনা সাইড-বাই-সাইড জন্য করতে হয়। ফলস্বরূপ আউটপুটটি হ'ল (খুব সহজেই স্পষ্ট করতে যে আমি কেবল কাঙ্ক্ষিত অংশগুলি স্পর্শ করেছি এবং অন্য কিছুই নয়) আমি মূল এক্সএমএলকে কিছুটা সংশোধন করেছি:

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;

আপনার সমাধানটি দেখতে দুর্দান্ত এবং এটি সহায়ক ছিল তবে আমি সিএলআর ব্যবহার করতে পারি।
ওয়াজটেক

@ ওয়াজটেক ধন্যবাদ বিকল্প থাকা ভাল, তাই না? কৌতূহলের বাইরে, আপনি এসকিউএলসিএলআর ব্যবহার করতে পারবেন না কেন?
সলোমন রুটজকি

এটি আমাদের স্থাপত্যের কারণে। আমরা মাল্টি টেন্যান্সির ওয়েব অ্যাপ্লিকেশন পেয়েছি। প্রত্যেক ভাড়াটের নিজস্ব ডেটাবেস থাকে। আমরা ইচ্ছুক প্রক্রিয়া চলাকালীন ব্যর্থ হতে পারে এমন কোনও 'চলন্ত অংশ' যুক্ত করতে চাই না। কেবলমাত্র-কোড / ওয়েব অ্যাপ্লিকেশন পদ্ধতির ব্যবহার আমাদের পক্ষে অনেক বেশি রক্ষণাবেক্ষণযোগ্য।
ওয়াজটেক

1

এসকিউএল সার্ভারের বাইরে এটি করার আরও ভাল উপায় সম্ভবত রয়েছে। তবে এটি করার একটি উপায় এখানে।

আপনার তথ্য:

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;

এক্সএমএল সিটিই আপনার এক্সএমএল ভেরিয়েবলকে একটি সারণিতে রূপান্তরিত করে।

প্রধান নির্বাচন তারপরে সিটিইটিকে আবার এক্সএমএলে রূপান্তরিত করে।

আউটপুট:

<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


আপনার সাহায্যের জন্য আপনাকে ধন্যবাদ যদিও আমি আমার প্রশ্নটি আপডেট করেছি - আমার মামলাটি জটিল। পারফরম্যান্সের কারণে আমি এসকিউএল সার্ভার ব্যবহার করে আমার এক্সএমএল আপডেট করতে চাই। আমি কয়েক হাজার রেকর্ড ধারণ করে এমন টেবিল পেয়েছি। অন্য বিকল্প হ'ল এএসপি এমভিসি অ্যাপ্লিকেশনের মধ্যে এটিকে লোড করা, ডিজিটালাইজ করা এবং সিরিয়ালাইজ করা।
ওয়াজটেক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.