使用子字符串

时间:2018-04-25 22:37:49

标签: sql-server xml tsql 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附加到名字和姓氏

&#34;规格&#34;是更新名字并将id(整数)列放在姓氏和名字的末尾(我已经从下面的SQLFiddle中解决了PoC,但没有完全,因为只有在有一条记录的情况下它才有效该表,现在只尝试使用firstname)

我全天都

我查看了互联网并且功能修改(替换......的值)一次只能影响其中一个帐户。因此,我必须创建一个这样的循环。但是如果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(&#39; // Account / FirstName / text()[substring(。,string-length(x)-1,11)]&#39;,&#39; nvarchar(255)) 从     XMLtbl

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

  

Msg 2203,Level 16,State 1,Line 114
  XQuery [XMLtbl.XMLCol.value()]:只有&#39; http://www.w3.org/2001/XMLSchema#decimal?&#39;,&#39; http://www.w3.org/2001/XMLSchema#boolean?&#39;或者&#39;节点()*&#39;表达式允许作为谓词,找到&#39; xs:string&#39;

以下是我正在处理的所有事情

这比使用EXEC创建SQL字符串并动态运行SQL要少一点,只是尝试尽可能做到最好。

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

很有必要我知道xquery / xpath并不容易,并且远离它并留给专家

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

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

1 个答案:

答案 0 :(得分:3)

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