不使用 sql:variable 将节点插入 XML

Insert node into XML without using sql:variable

我查看了整个互联网(包括 SO 但我可能错过了它)寻找一种将节点插入现有 XML 的方法,该节点包含在变量中而无需首先创建为 XML在变量中我想插入的节点字符串,并使用 "set @XMLVariable01.modify('insert sql:variable("@XMLVariable02") as ...".

从下面的例子中我想得到的最终结果是:

<P xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <L01>
    <A xsi:nil="true" />
    <B xsi:nil="true" />
    <C xsi:nil="true" />
  </L01>
  <L02>
    <A>2</A>
    <B xsi:nil="true" />
    <C>x</C>
  </L02>
</P>

示例:

declare
    @P xml;

declare
    @A varchar,
    @B varchar,
    @C varchar;

select
    @P = (
        select
            @A as [A],
            @B as [B],
            @C as [C]
        for xml path(N'L01'), root('P'), type, elements xsinil
        );

select @P; --Initial result

select
    @A = '2',
    @B = NULL,
    @C = 'x';

--select @P = ...?

select @P; --Final result

我做了以下事情:

select
    @P = (
        select (
            select
                v.query('.')
            from @P.nodes('P/L01') as t (v)),
            (
            select
                @A as [A],
                @B as [B],
                @C as [C]
            for xml path(N'L02'), type, elements xsinil
            )
        for xml path(N'P'), type, elements xsinil
        );

这很接近,但我不希望 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' 除了在 XML:

的 initial/top 行之外的任何地方
<P xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <L01>
    <A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
    <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
    <C xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
  </L01>
  <L02 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <A>2</A>
    <B xsi:nil="true" />
    <C>x</C>
  </L02>
</P>

编辑(2018Dec14~1343): 基于 Schnugo 的有用见解(是的,我 need/want NULL 值是为了记录所指示的元素,但没有收到任何值。另外,我很抱歉,但我无法完全理解所有这些在 1-5 中被建议。),我想出了以下内容:

select
    @P = (      
        select
            cast(replace(replace(cast(([XML].[Value]) as nvarchar(max)), cjR.Old01, cjR.New01), cjR.Old02, cjR.New02) as xml)
        from (
            select (
                select
                    v.query('.')
                from @P.nodes(N'P/L01') as t (v)),
                (
                select
                    @A as [A],
                    @B as [B],
                    @C as [C]
                for xml path(N'L02'), type, elements xsinil
                )
            for xml path(N'P'), type, elements xsinil
            ) as [XML] ([Value])
        cross join (
            select
                Value02 as Old01,
                N'' as New01,
                quotename(Value01, N'<>') as Old02,
                quotename(Value01 + Value02, N'<>') as New02
            from (
                select
                    N'P',
                    N' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
                ) as R (Value01, Value02)
            ) as cjR
        );

产生本应如此简单的操作的方法似乎很笨拙,但显然事实并非如此。我欢迎任何进一步的建议来改进这一点。

您在这里遇到了 SQL 服务器 XML 支持的各种缺陷...

对于嵌套 XML 没有其他方法可以使用子查询。但是子查询中的 重复命名空间 是一个非常烦人的问题。生成的 XML 是完美的,但是重复的名称空间声明确实会使您的 XML 非常膨胀。几个世纪以来,人们一直在讨论(并祈求更好的解决方案)。有一张 Connect-Ticket 已经有 10 多年了,票数很多。但这与连接一起消失了。

你真的需要 NULL 价值来炫耀 xsi:nil 吗?默认是忽略它们...

很多时候你会看到丑陋的解决方法
它们都使用 CASTNVARCHAR(MAX),使用字符串方法(STUFFSUBSTRING、...)操作名称空间并重新转换为 XML 最后。

  1. 默认命名空间:创建不带命名空间的 XML 并将顶级节点与命名空间一起添加
  2. 前缀命名空间:使用命名空间 placeholder 创建 XML 并再次使用字符串方法 (REPLACE) 更正此 ()
  3. 您的示例似乎需要 xsi:nil 来强制执行 NULL 不能省略的值。这更糟糕......您可以创建 XML,然后使用字符串方法来丢弃错误的声明。
  4. 您可以通过连接在字符串级别构建整个内容
  5. FOR XML EXPLICIT。这是一种奇怪且难以学习的格式。但这是我所知道的唯一一种按照您想要的方式正确设置命名空间的方法。

对不起,这正是XML SQL-服务器无法轻松构建的类型...

如果您需要有关提供的选项之一的帮助,您可以开始一个新问题,其中包含一个关于此的特定问题。