我将以下简化的 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,除非可以选择使用通配符,例如%?
请问有更有效的方法吗?
您可以更有效地编写它,方法是循环直到没有任何内容可更新,并使用
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;
或者您可以使用 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;