sql-server - T-SQL 2014 XQuery 使用子字符串的解决方案

标签 sql-server xml t-sql sql-server-2012 xquery

我正在尝试向格式如下的表中的 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 (基于下面的答案,但它还有一些我使用的与问题无关的字段)

http://sqlfiddle.com/#!18/afff6

最佳答案

你的问题很长,几乎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/

相关文章:

用于搜索具有无限数量位字段的表的 SQL 设计方法

java - xpath 给出的节点为空值

sql-server - 合并到没有行

sql-server - 字符串或二进制数据将被截断 : Error not caught by TRY_CAST

sql-server - 性能星型模式(BI)

javascript - 尝试读取 xml 文件时出现 AJAX 错误

java - 如何使用 XPath 提取 XML 表数据

sql - 即使出现错误,如何强制创建存储过程?

sql-server-2008 - 计算累计佣金金额

sql - 具有多个联接的SQL JOIN语法错误