SQL 中生成的 XML 结构不正确

Incorrect structure of XML generated in SQL

我遇到了另一个问题,我只是将我的 UNION 更改为 UNION ALL 并且它可以正常工作,但我想添加另一个 XML 路径(在开头)- 这将是两个查询的常量。

(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT 
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL].[dbo].[dk_documents] where id in (1,2,3)

UNION ALL

(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT 
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL2].[dbo].[dk_documents] where id in (1,2,3)
FOR XML PATH('test'))

它工作正常,但我想要这样的东西:

SELECT 1 as test,
(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT 
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL].[dbo].[dk_documents] where id in (1,2,3)

UNION ALL

(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
 FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT 
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL2].[dbo].[dk_documents] where id in (1,2,3)
FOR XML PATH('test'))
FOR XML PATH('anotherPath')

我收到这个错误:

    Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.

输出应该像

   <test>1</test>
     <>All of tese columns from QUERY with union ALL</>

举个例子:

WITH XMLNAMESPACES(DEFAULT 'Dummy') 

   SELECT 1 as test,
          2 as anotherOne,
   (SELECT * FROM 
   (SELECT id, symbol  from table1
    WHERE id in (1,2,3)
    UNION ALL
   SELECT id, nrdok from table2
   WHERE id in (4,5,6))as yolo
   FOR XML PATH(''),TYPE)
   FOR XML PATH('test')

它给了我一个输出:

 <test xmlns="Dummy">
   <test>1</test>
   <anotherOne>2</anotherOne>
   <id xmlns="Dummy">1</id>
   <symbol xmlns="Dummy">test10</symbol>
   <id xmlns="Dummy">2</id>
   <symbol xmlns="Dummy">test10</symbol>
   <id xmlns="Dummy">3</id>
   <symbol xmlns="Dummy">test10</symbol>
   <id xmlns="Dummy">4</id>
   <symbol xmlns="Dummy">test11</symbol>
   <id xmlns="Dummy">5</id>
   <symbol xmlns="Dummy">test11</symbol>
   <id xmlns="Dummy">6</id>
   <symbol xmlns="Dummy">test11</symbol>
</test>

我想要:

  <test xmlns="Dummy">
    <test>1</test>
    <anotherOne>2</anotherOne>
      <id>1</id>
      <symbol>test10</symbol>
      <id>2</id>
      <symbol>test10</symbol>
      <id>3</id>
      <symbol>test10</symbol>
      <id>4</id>
      <symbol>test11</symbol>
      <id>5</id>
      <symbol>test11</symbol>
      <id>6</id>
      <symbol>test11</symbol>
  </test>

您可以在下面的SQL Select语句中进行测试。

希望对你有帮助,

declare @xml xml
declare @xml1 xml
set @xml1 = '<test>1</test>'
declare @xml2 xml
set @xml2 = ( select top 4 name from sys.databases FOR XML PATH('database') )
set @xml = (SELECT @xml1, @xml2 FOR XML PATH(''))
select  @xml

输出结果如下

正如您在 中指出的那样,重复的命名空间并没有错,只是令人讨厌,而且 - 如果 URI 又多又长,它们会使您的 XML 变得异常庞大。 .

我已经在 link 和 之间放置了一个。诀窍是创建没有命名空间的 XML 并在最后 SELECT ... FOR XML PATH 添加命名空间:

但我必须承认,经过长时间的反复试验,我发现,如果涉及到 DEFAULT 命名空间,则似乎存在错误。我尝试过的任何方法都会导致重复的命名空间声明或重复的 空命名空间 声明。

所以我能找到的唯一解决方案是这个(我现在去洗手:-)):

DECLARE @table1 TABLE(id INT,symbol VARCHAR(100));
INSERT INTO @table1 VALUES
 (1,'Test 1')
,(2,'Test 2')
,(3,'Test 3')
,(4,'Test 4');

DECLARE @nordic_table2 TABLE(id INT,nrdok VARCHAR(100));
INSERT INTO @nordic_table2 VALUES
 (1,'Test 1')
,(2,'Test 2')
,(3,'Test 3')
,(4,'Test 4');


DECLARE @XmlWithoutNamespace XML=
(
 SELECT 1 as test
       ,2 as anotherOne
       ,(
           SELECT * 
           FROM 
           (
            SELECT id, symbol  from @table1
            WHERE id in (1,2,3)
            UNION ALL
            SELECT id, nrdok from @nordic_table2
            WHERE id in (4,5,6)
           ) AS yolo
           FOR XML PATH(''),TYPE
       )
 FOR XML PATH('')
);

SELECT
CAST(
    '<test xmlns="Dummy">'
    +
    CAST(@XmlWithoutNamespace AS NVARCHAR(MAX))
    +
    '</test>'
AS XML);

更新

我强烈建议你把你的结构改成这样

SELECT id AS [@id]
      ,symbol AS [*]
FROM 
(
SELECT id, symbol  from @table1
WHERE id in (1,2,3)
UNION ALL
SELECT id, nrdok from @nordic_table2
WHERE id in (4,5,6)
) AS yolo
FOR XML PATH('symbol'),TYPE

结果应该是这样的,这样读起来查询起来会好很多...

<test xmlns="Dummy">
  <test>1</test>
  <anotherOne>2</anotherOne>
  <symbol id="1">Test 1</symbol>
  <symbol id="2">Test 2</symbol>
  <symbol id="3">Test 3</symbol>
  <symbol id="4">Test 4</symbol>
</test>

更新 2:更多命名空间...

正确处理命名空间实际上非常困难——几乎是不可能的。 FOR XML PATH.modify() 等方法中有一些高度发达的逻辑。我尝试了几种方法,但没有找到令人信服的方法...

我找到的唯一方法非常难看。诀窍是,仅创建 1-level-XML(没有来自子选择的嵌套元素!)并将它们存储为字符串。但是在使用名称空间声明删除根之前。这样做你会得到无效的 XML 片段。

您连接它们并在最后一步中将全部 CAST 返回到 XML。

--Just a container to collect the XML parts
DECLARE @CollectXML TABLE(ID INT IDENTITY,Content XML,CleanedAsString NVARCHAR(MAX));

--The final ",ROOT('xyz')" will force the namespace's declaration into the root node
WITH XMLNAMESPACES('SomeTestUri' AS abc)
INSERT INTO @CollectXML(Content)
SELECT
(
     SELECT 1 as [abc:test]
           ,2 as anotherOne
     FOR XML PATH(''),ROOT('xyz'),TYPE
);
--No we use ugly string manipulation to cut out the inner part without the namespace declaration
UPDATE @CollectXML SET CleanedAsString=
(
    SELECT Rest
    FROM @CollectXML
    CROSS APPLY (SELECT CAST(Content AS NVARCHAR(MAX))) AS Casted(XmlAsString)
    CROSS APPLY (SELECT REVERSE(SUBSTRING(XmlAsString,CHARINDEX('>',XmlAsString)+1,LEN(XmlAsString)))) AS Cut1(part1Reverse)
    CROSS APPLY (SELECT REVERSE(SUBSTRING(part1Reverse,CHARINDEX('<',part1Reverse)+1,LEN(part1Reverse)))) AS Cut2(Rest)
    WHERE ID=1
)
WHERE ID=1;

--The same with the second part
WITH XMLNAMESPACES('SomeTestUri' AS abc)
INSERT INTO @CollectXML(Content)
SELECT 
(
    SELECT id AS [@abc:id]
          ,symbol AS [*]
    FROM 
    (
    SELECT id, symbol  from @table1
    WHERE id in (1,2,3)
    UNION ALL
    SELECT id, nrdok from @nordic_table2
    WHERE id in (4,5,6)
    ) AS yolo
    FOR XML PATH('abc:symbol'),ROOT('xyz'),TYPE --the not needed root will take the namespace declaration out of the deeper elements
);
--and the ugly string manipulation
UPDATE @CollectXML SET CleanedAsString=
(
    SELECT Rest
    FROM @CollectXML
    CROSS APPLY (SELECT CAST(Content AS NVARCHAR(MAX))) AS Casted(XmlAsString)
    CROSS APPLY (SELECT REVERSE(SUBSTRING(XmlAsString,CHARINDEX('>',XmlAsString)+1,LEN(XmlAsString)))) AS Cut1(part1Reverse)
    CROSS APPLY (SELECT REVERSE(SUBSTRING(part1Reverse,CHARINDEX('<',part1Reverse)+1,LEN(part1Reverse)))) AS Cut2(Rest)
    WHERE ID=2
)
WHERE ID=2;

--The XML is put together and - as the very last step! - casted back to XML
SELECT
CAST(
    '<test xmlns="Dummy" xmlns:abc="SomeTestUri">'
    +
    (SELECT CleanedAsString FROM @CollectXML WHERE ID=1)
    +
    (SELECT CleanedAsString FROM @CollectXML WHERE ID=2)
    +
    '</test>'
AS XML);

这个

的结果
<test xmlns="Dummy" xmlns:abc="SomeTestUri">
  <abc:test>1</abc:test>
  <anotherOne>2</anotherOne>
  <abc:symbol abc:id="1">Test 1</abc:symbol>
  <abc:symbol abc:id="2">Test 2</abc:symbol>
  <abc:symbol abc:id="3">Test 3</abc:symbol>
  <abc:symbol abc:id="4">Test 4</abc:symbol>
</test>