【问题标题】:Replace value of one node in array of nodes in T-SQL替换T-SQL中节点数组中一个节点的值
【发布时间】:2017-10-28 02:59:18
【问题描述】:

如果我在表中有一个列作为 xml 具有以下节点结构:

<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <PickListObjectBaseModel>
    <MasterObjectId>3964405</MasterObjectId>
    <IsSelected>false</IsSelected>
  </PickListObjectBaseModel>
  <PickListObjectBaseModel>
    <MasterObjectId>405716</MasterObjectId>
    <IsSelected>false</IsSelected>
  </PickListObjectBaseModel>
  <PickListObjectBaseModel>
    <MasterObjectId>5872525</MasterObjectId>
    <IsSelected>false</IsSelected>
  </PickListObjectBaseModel>
</ArrayOfPickListObjectBaseModel>

我可以成功检索到具有特定 MasterObjectId 的所有记录

SELECT  p.MyPrimaryKey, 
        m.value('(MasterObjectId)[1]', 'INT')  as MasterObjectID,
        p.MasterObjectIDs
    FROM PickList p
    CROSS APPLY MasterObjectIDs.nodes('//PickListObjectBaseModel') AS t1(m)
    WHERE m.value('(MasterObjectId)[1]', 'INT') = 3964405

但是,如果需要,我将如何替换所有匹配记录中的那个数字? 所以在上面的例子中,将3964405替换为999

这是不好的语法,但我认为它类似于:

    DECLARE @oldId INT = 13131180;
    DECLARE @newId INT = 99999;

    UPDATE PickList
        SET MasterObjectIDs.modify('replace value of 
    (//PickListObjectBaseModel/MasterObjectId/text()=sql:variable("@oldId"))[1] with sql:variable("@newId")');

【问题讨论】:

    标签: sql-server xml tsql xpath xquery


    【解决方案1】:

    这里是基于值替换文本节点的完整解决方案 FROM 部分将加速它,因为它减少了受影响的行数,否则它将对所有行执行操作

        DECLARE @oldId INT = 13131180;
        DECLARE @newId INT = 99999;
    
        UPDATE PickList
            SET MasterObjectIDs.modify('replace value of 
            (//PickListObjectBaseModel/MasterObjectId[text() = sql:variable("@oldId")]/text())[1]
                with sql:variable("@newId")')
            FROM PickList p
                    CROSS APPLY MasterObjectIDs.nodes('//PickListObjectBaseModel') AS t1(m)
                    WHERE m.value('(MasterObjectId)[1]', 'INT') = @oldId
    

    【讨论】:

      【解决方案2】:

      太好了,您找到了解决方案(投票赞成),只是一些提示:

      一个三行的虚拟表:

      DECLARE @DummyTable TABLE(ID INT IDENTITY, Comment VARCHAR(100), MasterObjectIDs XML);
      INSERT INTO @DummyTable VALUES
      ('Your example', N'<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <PickListObjectBaseModel>
          <MasterObjectId>3964405</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
        <PickListObjectBaseModel>
          <MasterObjectId>405716</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
        <PickListObjectBaseModel>
          <MasterObjectId>5872525</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
      </ArrayOfPickListObjectBaseModel>'
      )
      ,('More than one target', N'<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <PickListObjectBaseModel>
          <MasterObjectId>3964405</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
        <PickListObjectBaseModel>
          <MasterObjectId>405716</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
        <PickListObjectBaseModel>
          <MasterObjectId>3964405</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
      </ArrayOfPickListObjectBaseModel>'
      ),
      ('no target', N'<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <PickListObjectBaseModel>
          <MasterObjectId>1111</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
        <PickListObjectBaseModel>
          <MasterObjectId>405716</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
        <PickListObjectBaseModel>
          <MasterObjectId>5872525</MasterObjectId>
          <IsSelected>false</IsSelected>
        </PickListObjectBaseModel>
      </ArrayOfPickListObjectBaseModel>'); 
      

      --变量

      DECLARE @oldId INT = 3964405;
      DECLARE @newId INT = 99999;
      

      --我使用xml.exist(),比您过滤目标行的方法更快:

      UPDATE @DummyTable
              SET MasterObjectIDs.modify('replace value of 
              (//PickListObjectBaseModel/MasterObjectId[text() = sql:variable("@oldId")]/text())[1]
                  with sql:variable("@newId")')
      FROM @DummyTable p
      WHERE p.MasterObjectIDs.exist('/ArrayOfPickListObjectBaseModel/PickListObjectBaseModel/MasterObjectId[text()=sql:variable("@oldId")]') = 1;
      

      --结果

      SELECT @@ROWCOUNT;         --good the filter hits only 2 rows
      SELECT * FROM @DummyTable; --bad, the XML more than one occurances is not edited everywhere...
      

      如果您要查找的 id 可能在一个 XML 中出现多次,您的方法将不适用于所有这些,仅适用于第一个...

      您可以循环运行您的语句,直到exist() 返回0,或者您可以分解 XML 并从头开始重建它,如下所示:

      WITH UpdateableCTE AS
      (
          SELECT p.MasterObjectIds AS Original
                ,B.NewXML
          FROM @DummyTable AS p
          CROSS APPLY
          (
              SELECT
              (
                  SELECT CASE WHEN plo.value(N'(MasterObjectId/text())[1]','int')=@oldId THEN @newId ELSE plo.value(N'(MasterObjectId/text())[1]','int') END AS MasterObjectId
                        ,plo.value(N'(IsSelected/text())[1]','nvarchar(max)') AS IsSelected
                  FROM MasterObjectIDs.nodes(N'/ArrayOfPickListObjectBaseModel/PickListObjectBaseModel') AS A(plo)
                  FOR XML PATH(N'PickListObjectBaseMode'),ROOT(N'ArrayOfPickListObjectBaseModel'),TYPE
              )
          ) AS B(NewXML)
          WHERE p.MasterObjectIDs.exist('/ArrayOfPickListObjectBaseModel/PickListObjectBaseModel/MasterObjectId[text()=sql:variable("@oldId")]') = 1
      )
      UPDATE UpdateableCTE SET Original=NewXML;
      

      --只有2行受到影响

      SELECT @@ROWCOUNT;         --good the filter hits only 2 rows
      SELECT * FROM @DummyTable; --all occurances are switched...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-20
        • 1970-01-01
        • 2018-08-24
        • 2011-03-13
        • 1970-01-01
        • 1970-01-01
        • 2013-07-29
        • 1970-01-01
        相关资源
        最近更新 更多