SQL 无循环更新多个 XML 节点值

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

我将以下简化的 XML 存储在 SQL 中的 XML 列中:

<data>
  <SchoolName>My First School</SchoolName>
  <Table TableId="Classes">
    <TableRow RowNumber="1">
        <ClassName>Oak</ClassName>
        <TeacherName>Jane Smith</TeacherName>
    </TableRow>
    <TableRow RowNumber="2">
        <ClassName>Oak</ClassName>
        <TeacherName>Fred Green</TeacherName>
    </TableRow>
    <TableRow RowNumber="3">
        <ClassName>Oak</ClassName>
        <TeacherName>Mary Peters</TeacherName>
    </TableRow>
  </Table>
  <Table TableId="Pupils">
    <TableRow RowNumber="1">
      <PupilName>David</PupilName>
    </TableRow>
    <TableRow RowNumber="2">
        <PupilName>Paul</PupilName>
    </TableRow>
    <TableRow RowNumber="3">
       <PupilName>Sam</PupilName>
    </TableRow>
  </Table>
</data>

我希望通过单个脚本将“Classes”XML 表中所有 TableRows 的“ClassName”值从“Oak”更新为“Maple”。

我写了下面的内容,它有效,但在大量行上非常耗时。

DECLARE @COUNT INT
SET @COUNT = 3 --maximum number of rows

WHILE @COUNT > 0
BEGIN
PRINT CAST(@COUNT AS NVARCHAR(30))

    UPDATE TableName    
    SET XMLColumnName.modify('replace value of (/data/Table[@TableId=("Classes")]/TableRow[@RowNumber=sql:variable("@COUNT")]/ClassName/text())[1] with "Maple"')
    WHERE ...
--move to the next table row
    SET @COUNT = @COUNT - 1
    
END

不幸的是,初始的 ClassValue 不一样,所以我无法查找和替换 Oak,除非可以选择使用通配符,例如%?

请问有更有效的方法吗?

sql xml t-sql sql-update sqlxml
1个回答
0
投票

您可以更有效地编写它,方法是循环直到没有任何内容可更新,并使用

WHERE exists
过滤器仅更新需要更新的行。

WHILE 1=1
BEGIN
    UPDATE TableName
    SET XMLColumnName.modify('
      replace value of
      (data/Table [@TableId = "Classes"] /TableRow/ClassName/text()[. = "Oak"])[1]
      with "Maple"
    ')
    WHERE XMLColumnName.exist('
      data/Table [@TableId = "Classes"] /TableRow/ClassName/text()[. = "Oak"]
    ') = 1;

    IF @@ROWCOUNT = 0
        BREAK;
END;

db<>小提琴

或者您可以使用 XQuery 重建 XML,但是对于如此深层的嵌套结构,这会变得很复杂。

在每个级别上,检查是否有所需的节点。如果是,则创建一个替换节点(如果需要,可以下到另一个级别)。否则就退回你所拥有的。

UPDATE TableName
SET XMLColumnName = 
  XMLColumnName.query('
  <data>
  {
    for $t in data/*
    return
      if ($t/@TableId = "Classes")
      then
        <Table>
        {
          for $r in $t/*
          return
            if ($r/ClassName/text()[. = "Oak"])
            then
              <TableRow RowNumber = "{$r/@RowNumber}">
              {
                for $n in $r/*
                return
                  if (local-name($n) = "ClassName")
                  then
                   <ClassName>Maple</ClassName>
                  else $n
              }
              </TableRow>
            else $r
        }
        </Table>
      else $t
  }
  </data>
    ')
WHERE XMLColumnName.exist('
  data/Table [@TableId = "Classes"] /TableRow/ClassName/text()[. = "Oak"]
') = 1;

db<>小提琴

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