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

问题描述 投票:0回答:1

我想在一个表中更改2列的值。一列是varchar,另一列是XML。首先,我想用新值替换RECIPIENT列的值,并用新值RecipientNo替换XML列中名为RecipientNo的节点值。如何在同一更新功能中执行这两项操作?下面的查询有效。其次,DATARECORD表包含太多记录。修改功能是否花费太多时间来更新记录?如果是这样,我如何提高修改功能的性能,或者您可以建议其他替代解决方案?顺便说一句,我不能将索引添加到DATARECORD表。谢谢。

这里是示例行;

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
sql sql-server
1个回答
0
投票

当然可以在同一条更新语句中更新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

哪个产量:

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>

有几件事导致您的性能问题:

  • 将SQL Server本质上以UTF-16编码存储的XML转换为varchar(两次)非常昂贵。它还会删除数据库归类之外的所有Unicode字符。
  • 在XML上执行like匹配(转换为varchar)将导致TABLE SCAN操作,转换和测试表中的每一行。

要考虑的一些事情:

  • XML Index(es)添加到RecordDetails列,并使用类似WHERE RecordDetails.exists('/MetaTag/Code[.="123"])的名称来短列出要更新的行。
  • 或者,将您的RecordDetails预切碎,将/MetaTag/Code/text()的值保留在表列中(例如:MetaTagCode),并在查询中使用类似WHERE MetaTagCode='123'的内容。向该列添加索引将使SQL在搜索所需值(而不是表扫描)时执行便宜得多的INDEX SCAN。

由于您说无法添加索引,因此基本上必须忍受TABLE SCAN,然后等待它。

© www.soinside.com 2019 - 2024. All rights reserved.