我在SQL Server 2008中使用FOR XML
愚弄,看看它是否更适合构建Web服务响应而不是依赖于Hibernate&用于映射DTO的HQL(或从平面结果集中手动映射它们)。
我创建了一个虚构的例子,人们可能有孩子和一组电话号码。
我遇到SELECT name FROM personCte
会产生不需要的包裹<name>
元素的情况,导致<name><name first="test" last="test"/></name>
。
通过执行以下操作,我可以摆脱额外的包装元素,但我想知道是否有更合适的方法吗?
SELECT (select name)
FROM personCte
该解决方案的一个问题是它不能在CTE中使用,因为必须命名所有CTE列。
我还想知道是否有更好的方法可以将多个属性解压缩到单个元素(例如firstName和lastName到name)而不是执行子查询?
以下是我正在使用的示例代码:
DECLARE @Person TABLE (
id int NOT NULL PRIMARY KEY IDENTITY(1, 1),
firstName nvarchar(50) NOT NULL,
lastName nvarchar(50) NOT NULL,
parentId int NULL
);
DECLARE @PersonPhoneNumber TABLE (
personId int NOT NULL,
number char(12) NOT NULL
);
INSERT INTO @Person (firstName, lastName, parentId)
VALUES
('Person', 'A', NULL),
('Person', 'B', 1),
('Person', 'C', 2);
INSERT INTO @PersonPhoneNumber
VALUES
(1, '888-888-8888'),
(1, '999-999-9999'),
(3, '333-333-3333');
;WITH personCte AS (
SELECT
id,
(
SELECT firstName AS [@first], lastName AS [@last]
FROM @Person
WHERE id = person.id
FOR XML PATH('name'), TYPE
) AS name,
(
SELECT number
FROM @PersonPhoneNumber
WHERE personId = person.id
FOR XML PATH(''), TYPE
) AS phoneNumbers,
parentId
FROM @Person person
)
SELECT
id,
(SELECT name), /* Used to avoid unwanted wrapping name element */
phoneNumbers,
parentId,
(
SELECT id, (SELECT name), phoneNumbers, parentId
FROM personCte person
WHERE parentId = p.id
FOR XML AUTO, TYPE
) AS children
FROM personCte p
FOR XML AUTO, ROOT('persons'), TYPE
哪个正确产生:
<persons>
<person id="1">
<name first="Person" last="A" />
<phoneNumbers>
<number>888-888-8888</number>
<number>999-999-9999</number>
</phoneNumbers>
<children>
<person id="2" parentId="1">
<name first="Person" last="B" />
</person>
</children>
</person>
<person id="2" parentId="1">
<name first="Person" last="B" />
<children>
<person id="3" parentId="2">
<name first="Person" last="C" />
<phoneNumbers>
<number>333-333-3333</number>
</phoneNumbers>
</person>
</children>
</person>
<person id="3" parentId="2">
<name first="Person" last="C" />
<phoneNumbers>
<number>333-333-3333</number>
</phoneNumbers>
</person>
</persons>
答案 0 :(得分:1)
您可以使用query()
XML方法排除不需要的嵌套:
select p.id, p.name.query('.')
FROM personCte p
FOR XML AUTO, ROOT('persons'), TYPE;
编辑:你需要的是用PATH()
语法重写所有内容。在这种情况下,您不需要任何方法,并且您还可以指定嵌套节点,这些节点不能与AUTO
一起使用。因此,您的完整查询将如下所示:
;WITH personCte AS (
SELECT id, parentId, firstName, lastName, (
SELECT number
FROM @PersonPhoneNumber
WHERE personId = person.id
FOR XML PATH(''), TYPE
) AS phoneNumbers
FROM @Person person
)
SELECT
p.id as [@id],
p.parentId as [@parentid],
p.firstName AS [name/@first],
p.lastName AS [name/@last],
p.phoneNumbers,
(
SELECT id as [@id], parentId as [@parentid],
person.firstName AS [name/@first],
person.lastName AS [name/@last],
phoneNumbers
FROM personCte person
WHERE parentId = p.id
FOR XML path('person'), TYPE
) AS children
FROM personCte p
FOR XML path('person'), ROOT('persons'), TYPE;