简体   繁体   English

如何在同一更新查询中使用另一列新值更新 xml 列节点值?

[英]How to update xml column node value with another column new value at same update query?

I want to change the value of 2 columns in one table.我想更改一张表中 2 列的值。 One column is varchar and the other is XML.一列是 varchar,另一列是 XML。 First of all, I want to replace the value of the RECIPIENT column with the new value and replace the node value named as RecipientNo in the XML column with the new value of RecipientNo.首先,我想将 RECIPIENT 列的值替换为新值,并将 XML 列中名为 RecipientNo 的节点值替换为 RecipientNo 的新值。 How can I do these two operations in the same update function?如何在同一个更新函数中执行这两个操作? The query below works.下面的查询有效。 Secondly, DATARECORD table includes too many records.其次,DATARECORD 表包含的记录太多。 Does modify function take too much time to update the records?修改功能是否需要太多时间来更新记录? If so, how can I increase the performance of modify function or can you suggest another alternative solution?如果是这样,我如何提高修改功能的性能,或者您能否提出另一种替代解决方案? By the way, I cannot add index to DATARECORD table.顺便说一下,我无法向 DATARECORD 表添加索引。 Thanks.谢谢。

Here is the sample row;这是示例行;

ID   RECIPIENT   RECORDDETAILS 
1       1         <?xml version="1.0"?>
                  <MetaTag xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                         xmlns:xsd="http://www.w3.org/XMLSchema"> 
                       <Code>123</Code>
                       <RecipientNo>123</RecipientNo> 
                       <Name>xyz</Name>
                  </MetaTag>'
    CREATE TABLE #TEMPTABLE(
        ID bigint,
        RECIPIENT nvarchar(max),
        RECORDDETAILS xml
        )

    INSERT INTO #TEMPTABLE
    SELECT ID,RECIPIENT,RECORDDETAILS
    FROM DATARECORD WITH (NOLOCK)
    WHERE cast(RECORDDETAILS as varchar(max)) LIKE '%<Code>123</Code>%' and cast(RECORDDETAILS as varchar(max))  LIKE '%MetaTag%' 

    UPDATE #TEMPTABLE SET RECIPIENT = CONCAT('["queryType|1","recipientNoIDENTIFICATION|',RECIPIENT,']')

    UPDATE #TEMPTABLE SET RECORDDETAILS.modify('replace value of (MetaTag/RecipientNo/text())[1] with sql:column("RECIPIENT")')

    UPDATE d
    SET d.RECORDDETAILS =Concat('<?xml version="1.0"?>', CAST(t.RECORDDETAILS AS VARCHAR(max))),
    d.RECIPIENT = t.RECIPIENT
    FROM dbo.DATARECORD as d
    Join #TEMPTABLE as t
    ON t.ID = d.ID

It's certainly possible to update an SQL column and an XML node in the same update statement, eg:当然可以在同一个更新语句中更新 SQL 列和 XML 节点,例如:

create table DataRecord (
  ID bigint not null primary key,
  Recipient nvarchar(max) not null,
  RecordDetails xml not null
);

insert DataRecord values
  (1, N'1', N'<?xml version="1.0"?>
                  <MetaTag xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                         xmlns:xsd="http://www.w3.org/XMLSchema"> 
                       <Code>123</Code>
                       <RecipientNo>123</RecipientNo> 
                       <Name>xyz</Name>
                  </MetaTag>');

create table #TempTable (
  ID bigint not null primary key,
  Recipient nvarchar(max) not null,
  RecordDetails xml not null
);

insert #TempTable
  select ID, Recipient, RecordDetails
  from DataRecord with (nolock)
  where cast(RecordDetails as varchar(max)) like '%<Code>123</Code>%' and cast(RecordDetails as varchar(max)) like '%MetaTag%'

-- Change an SQL value and an XML node in the one update statement...
update tt set
  Recipient = NewRecipient,
  RecordDetails.modify('replace value of (/MetaTag/RecipientNo/text())[1] with sql:column("NewRecipient")')
from #TempTable tt
outer apply (
  select NewRecipient = concat('["queryType|1","recipientNoIDENTIFICATION|', Recipient, '"]')
) Calc

select * from #TempTable

Which yields:其中产生:

ID  Recipient                                       RecordDetails
1   ["queryType|1","recipientNoIDENTIFICATION|1"]   <MetaTag
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/XMLSchema">
  <Code>123</Code>
  <RecipientNo>["queryType|1","recipientNoIDENTIFICATION|1"]</RecipientNo>
  <Name>xyz</Name>
</MetaTag>

There are a couple of things contributing to your performance problem:有几件事会导致您的性能问题:

  • Converting XML, which SQL Server essentially stores in UTF-16 encoding, to varchar (twice) is expensive.将 SQL Server 本质上以 UTF-16 编码存储的 XML 转换为 varchar(两次)是很昂贵的。 It will also trash any Unicode characters outside your database's collation.它还将删除数据库排序规则之外的任何 Unicode 字符。
  • Performing like matches on the XML (converted to varchar) will be causing TABLE SCAN operations, converting and testing every row in your table.在 XML 上执行like匹配(转换为 varchar)将导致 TABLE SCAN 操作,转换和测试表中的每一行。

Some things to consider:需要考虑的一些事项:

  • Add XML Index(es) to the RecordDetails column and use something like WHERE RecordDetails.exists('/MetaTag/Code[.="123"]) to short list the rows to be updated.XML Index(es)添加到RecordDetails列并使用类似WHERE RecordDetails.exists('/MetaTag/Code[.="123"])来列出要更新的行。
  • Alternatively, pre-shred your RecordDetails , persist the value of /MetaTag/Code/text() in a table column (eg: MetaTagCode ), and use something like WHERE MetaTagCode='123' in your query.或者,预先粉碎您的RecordDetails ,将/MetaTag/Code/text()的值保留在表列中(例如: MetaTagCode ),并在您的查询中使用类似WHERE MetaTagCode='123'的内容。 Adding an index to that column will allow SQL to do a much cheaper INDEX SCAN when searching for the desired value instead of a TABLE SCAN.向该列添加索引将允许 SQL 在搜索所需值而不是 TABLE SCAN 时执行更便宜的 INDEX SCAN。

Since you say you cannot add indexes you're basically going to have to tolerate TABLE SCANs and just wait it out.既然你说你不能添加索引,你基本上将不得不容忍 TABLE SCANs 并等待它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM