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>
我遇到了另一个问题,我只是将我的 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
输出结果如下
正如您在
我已经在 link 和 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>