【问题标题】:T-SQL 2014 XQuery solution to using substringT-SQL 2014 XQuery 使用子字符串的解决方案
【发布时间】:2018-10-06 12:30:41
【问题描述】:

我正在尝试将更新语句写入格式如下的表中的 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 中解决了这个问题,但并不完全因为它只是如果表中有一条记录,则工作,现在只尝试名字)

我花了一整天

我查看了互联网,功能 modify(replace value of...) 一次只能影响 1 个帐户。因此,我不得不像这样创建一个循环。但是如果 SQL Server 支持 XPath 函数 matches() 并且可以使用 正则表达式 检查数字是否存在,那么它非常丑陋,并且底部变量 KeepGoing 会工作得更好,但这不在SQL Server 2017。我的工作将使用contains() 10 次!看看我是否完成并可以继续下一条记录。

如果有人有更好的优雅解决方案,请告诉我。请求这个的人想要这只是一个语句,没有游标。我已经从下面的代码中当然已经放弃了。

1) 从下面返回的 XML 中返回的查询是什么

4
4

4 用于 FirstName 中的最后一个字符

4 表示第二个节点中 FirstName 中的最后一个字符

这个选择也应该返回

5

5

下一条记录

但这不是主要问题,这是一个尝试在 xpath 中使用子字符串的参考

我试图通过 while 循环进行评估,所以我知道何时停止在 1 上执行 通过在最后一个字符上查找整数,我尝试在下面写失败

选择 XMLCol.value('//Account/FirstName/text()[substring(., string-length(x)-1,11)]', 'nvarchar(255)) 从 XMLtbl

我一直在尝试以下所有形式,只是为了得到这样的消息......

消息 2203,第 16 级,状态 1,第 114 行
XQuery [XMLtbl.XMLCol.value()]:只有 'http://www.w3.org/2001/XMLSchema#decimal?'、'http://www.w3.org/2001/XMLSchema#boolean?'或 'node()*' 表达式允许作为谓词,找到 'xs:string'

这是我正在处理的所有内容

这比制作 SQL 字符串和使用 EXEC 动态运行 SQL 稍微少一点,只是尽量做到最好。

http://sqlfiddle.com/#!18/7eed1

非常感谢我知道 xquery/xpath 并非易事,因此远离它并留给专家们使用

最后的小提琴(基于下面的答案,但它还有一些我使用的与问题无关的字段)

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

【问题讨论】:

    标签: sql-server xml tsql sql-server-2012 xquery


    【解决方案1】:

    您的问题很长,几乎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”来创建值,该值可以在之后的 UPDATE 中使用。

    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);
    

    【讨论】:

    • 添加的 ID 不是必需的,对于长消息感到抱歉,但这给了我一些弹药,我会尝试,实际上 ID 正在添加到您放置的位置 _blah
    • @MichaelEvanchik 提供了一些现实的(但减少了!)输入和操作后您期望的输出。这有助于超过一百行散文......
    • @MichaelEvanchik 你的评论可能对你来说很清楚,但我不是坐在你身边,我不知道你在想什么 - 对不起,我不知道你需要什么.. . 使用问题的编辑选项添加输入和输出...
    • @MichaelEvanchik 是否有原因,在您的编辑中,ID 没有附加到每个姓氏,这背后有什么逻辑吗?
    • @MichaelEvanchik 关于你的不,那只是一个错字。对于您的下一个问题:请非常小心您发布的内容。像我这样的人试图理解背后的逻辑,只是一个错字有点烦人......
    猜你喜欢
    • 2015-10-12
    • 1970-01-01
    • 2016-08-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2011-04-27
    • 1970-01-01
    • 2011-10-15
    相关资源
    最近更新 更多