0

FOR XML在 SQL Server 2008 中闲逛,看看它是否更适合构建 Web 服务响应,而不是依赖 Hibernate 和 HQL 来映射 DTO(或从平面结果集中手动映射它们)。

我创建了一个虚构的例子,其中人们可能有孩子和一组电话号码。

我面临的情况是SELECT name FROM personCte会产生不需要的包装<name>元素,从而导致<name><name first="test" last="test"/></name>.

我可以通过执行以下操作来摆脱额外的包装元素,但我想知道是否有更合适的方法?

SELECT (select name)
FROM personCte

该解决方案的一个问题是它不能在 CTE 中使用,因为必须命名所有 CTE 列。

我还想知道是否有比执行子查询更好的方法将多个属性展开为单个元素(例如将名字和姓氏转换为名称)?

这是我正在使用的示例代码:

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>
4

1 回答 1

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;
于 2015-07-15T23:44:16.867 回答