我正在尝试向格式如下的表中的 XML 列写入更新语句。
这是在表格的字段中
<Params>
<Account>
<FirstName>Michael</FirstName>
<LastName>Bar</LastName>
</Account>
<Account>
<FirstName>Pam</FirstName>
<LastName>Foo</LastName>
</Account>
</Params>
**this is in on field of ONE of another record in that table
<Params>
<Account>
<FirstName>Darold</FirstName>
<LastName>ok</LastName>
</Account>
<Account>
<FirstName>Fred</FirstName>
<LastName>whatever</LastName>
</Account>
<Account>
<FirstName>amy</FirstName>
<LastName>whatever</LastName>
</Account>
</Params>
**
期望的输出
<Params>
<Account>
<FirstName>Michael2334</FirstName>
<LastName>Bar2334</LastName>
</Account>
<Account>
<FirstName>Pam2334</FirstName>
<LastName>Foo2334</LastName>
</Account>
</Params>
<Params>
<Account>
<FirstName>Darold2335</FirstName>
<LastName>ok2335</LastName>
</Account>
<Account>
<FirstName>Fred2335</FirstName>
<LastName>whatever2335</LastName>
</Account>
<Account>
<FirstName>amy2335</FirstName>
<LastName>whatever2335</LastName>
</Account>
</Params>
我的最终目标是更新整个表的 xml 字段,该字段包含第 n 个带有连接表 ID 列的节点,并将该 ID 附加到名字和姓氏
“规范”是更新名字并将 id(整数)列放在姓氏和名字的末尾(我已经在下面的 SQLFiddle 的 PoC 中解决了这个问题,但并不完全,因为它只是如果表中有一条记录,并且现在只尝试名字,则有效)
我花了一整天
我查了一下互联网,修改功能(替换...的值)一次只能影响其中一个帐户。因此,我必须创建一个像这样的循环。但它非常丑陋,如果 SQL Server 支持 XPath 函数 matches()
并且可以使用正则表达式检查数字是否存在,那么底部变量 KeepGoing 的效果会好得多strong> 但这不在 SQL Server 2017 中。我的解决方法是使用 contains()
10 次!看看我是否完成并可以继续下一个记录。
如果有人有更好的优雅解决方案,请告诉我。请求此操作的人希望这只是一条语句,没有游标。我已经从下面的代码中当然已经放弃了。
1) 从下面的 XML 返回的查询是什么
4
4
4
代表名字中的最后一个字符
和
4
表示第二个节点中 FirstName 的最后一个字符
此选择也应该返回
5
5
下一条记录
但这不是主要问题,这是一个尝试在 xpath 中使用 substring 和 的仅供引用
我试图通过 while 循环进行评估,以便我知道何时停止在 1 上执行 通过查找最后一个字符上的整数,我尝试在下面写入失败
选择 XMLCol.value('//Account/FirstName/text()[substring(., string-length(x)-1,11)]', 'nvarchar(255)) 从 XMLtbl
我一直在尝试下面的所有形式,只是为了得到这样的消息......
Msg 2203, Level 16, State 1, Line 114
XQuery [XMLtbl.XMLCol.value()]: Only 'http://www.w3.org/2001/XMLSchema#decimal?', 'http://www.w3.org/2001/XMLSchema#boolean?' or 'node()*' expressions allowed as predicates, found 'xs:string'
这是我正在做的一切
这比创建 SQL 字符串并使用 EXEC
动态运行 SQL 的时间要少一点,只是尝试以尽可能最好的方式完成它。
http://sqlfiddle.com/#!18/7eed1
非常感谢,我知道 xquery/xpath 并不容易,所以远离它并将其留给专家
最终的 fiddle (基于下面的答案,但它还有一些我使用的与问题无关的字段)
最佳答案
你的问题很长,几乎TL;DR,但仍然不是很清楚......这将有助于提供预期的输出......
一些假设和陈述:
- 是的,
.modify()
允许每次调用进行一次更改 - 这是一个相当大的限制... - 您想要在多个位置更新 XML
- 根据您的规范,您希望将 ID 添加到 XML
- 不知道您想要通过最后一个字母方法实现什么目标
但是,在您的情况下,最好分解 XML 并从头开始重新创建它。为此,您可以使用任何 T-SQL
功能:
DECLARE @tbl TABLE(ID INT IDENTITY, YourXML XML);
INSERT INTO @tbl VALUES
(N'<Params>
<Account>
<FirstName>Michael</FirstName>
<LastName>Bar</LastName>
</Account>
<Account>
<FirstName>Pam</FirstName>
<LastName>Foo</LastName>
</Account>
</Params>')
,(N'<Params>
<Account>
<FirstName>John</FirstName>
<LastName>Wood</LastName>
</Account>
<Account>
<FirstName>Jane</FirstName>
<LastName>Smith</LastName>
</Account>
<Account>
<FirstName>Jim</FirstName>
<LastName>Stone</LastName>
</Account>
</Params>');
--查询使用“可更新的CTE”来创建值,该值可以在之后的更新中使用。
WITH cte AS
(
SELECT t.YourXML
,(
SELECT
--just silly, to show how it works: append "blah" to the first name
Acc.value(N'(FirstName/text())[1]',N'nvarchar(max)') + '_blah' AS FirstName
--Just pick the last character as LastName
,RIGHT(Acc.value(N'(LastName/text())[1]',N'nvarchar(max)'),1) AS LastName
--Add the ID in the last place
,t2.ID
FROM @tbl AS t2
CROSS APPLY t2.YourXML.nodes(N'/Params/Account') AS A(Acc)
WHERE t2.ID=t.ID
FOR XML PATH('Account'),ROOT('Params'),TYPE
) AS NewXML
FROM @tbl AS t
)
UPDATE cte SET YourXML=NewXML;
SELECT * FROM @tbl;
操作后其中一个 XML 看起来像这样
<Params>
<Account>
<FirstName>Michael_blah</FirstName>
<LastName>r</LastName>
<ID>1</ID>
</Account>
<Account>
<FirstName>Pam_blah</FirstName>
<LastName>o</LastName>
<ID>1</ID>
</Account>
</Params>
更新
此查询会将行的 ID(填充为五位数)附加到两个名称部分:
WITH cte AS
(
SELECT t.YourXML
,(
SELECT
Acc.value(N'(FirstName/text())[1]',N'nvarchar(max)') + REPLACE(STR(t2.ID,5),' ','0') AS FirstName
,Acc.value(N'(LastName/text())[1]',N'nvarchar(max)') + REPLACE(STR(t2.ID,5),' ','0') AS LastName
FROM @tbl AS t2
CROSS APPLY t2.YourXML.nodes(N'/Params/Account') AS A(Acc)
WHERE t2.ID=t.ID
FOR XML PATH('Account'),ROOT('Params'),TYPE
) AS NewXML
FROM @tbl AS t
)
UPDATE cte SET YourXML=NewXML;
SELECT * FROM @tbl;
此查询将提供每个名称的最后一个字符
SELECT Acc.value(N'substring((FirstName/text())[1],string-length((FirstName/text())[1]),1)',N'char(1)')
FROM @tbl AS t
CROSS APPLY t.YourXML.nodes(N'/Params/Account') AS A(Acc);
关于sql-server - T-SQL 2014 XQuery 使用子字符串的解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50032281/