Группировка SQL Server 2005 “ДЛЯ XML-ПУТИ”
-
22-07-2019 - |
Вопрос
Я пытаюсь сгенерировать XML-документ из базы данных SQL Server 2005, используя конструкцию "ДЛЯ XML".
В базе данных есть две простые таблицы с отношением "один ко многим":
1) Журналы
| Id | Number | Name |
----------------------------
| 53 | 0001 | Magazine 1 |
| 54 | 0002 | Magazine 2 |
| 55 | 0003 | Magazine 3 |
2) Изделия
| Id | Title | MagazineId | Size |
--------------------------------------
| 1 | Article 1 | 53 | 1205 |
| 2 | Article 2 | 53 | 817 |
| 3 | Article 3 | 54 | 1570 |
| 4 | Article 4 | 54 | 2510 |
| 5 | Article 5 | 55 | 910 |
Давайте предположим, что мне нужно найти все журналы, в которых есть статьи размером более 1000 и создать следующий XML:
<Magazines>
<Magazine Id="53">
<Number>0001</Number>
<Articles>
<Article Id="1">
<Title>Article 1</Title>
<Size>1205</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="3">
<Title>Article 3</Title>
<Size>1570</Size>
</Article>
<Article Id="4">
<Title>Article 4</Title>
<Size>2510</Size>
</Article>
</Articles>
</Magazine>
</Magazines>
Я пытаюсь создать такой xml, используя режим "ПУТЬ":
SELECT Magazines.Id AS "@Id",
Magazines.Number AS "Number",
Articles.Id AS "Articles/Article/@Id",
Articles.Title AS "Articles/Article/Title",
Articles.Size AS "Articles/Article/Size"
FROM Magazines INNER JOIN Articles ON Magazines.Id = Articles.MagazineId
WHERE Articles.Size > 1000
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
Это приведет к созданию следующего xml-файла:
<Magazines>
<Magazine Id="53">
<Number>0001</Number>
<Articles>
<Article Id="1">
<Title>Article 1</Title>
<Size>1205</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="3">
<Title>Article 3</Title>
<Size>1570</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="4">
<Title>Article 4</Title>
<Size>2510</Size>
</Article>
</Articles>
</Magazine>
</Magazines>
Итак, есть два элемента для журнала с Id ="54" (по одному для каждой статьи), и в этом проблема.
Я могу переписать запрос, используя подзапрос, подобный этому:
SELECT M.Id AS "@Id",
M.Number AS "Number",
(SELECT Articles.Id AS "@Id",
Articles.Title AS "Title",
Articles.Size AS "Size"
FROM Articles
WHERE Articles.MagazineId = M.Id
FOR XML PATH('Article'), ROOT('Articles'), TYPE
)
FROM Magazines AS M
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
И это приведет к получению следующего xml:
<Magazines>
<Magazine Id="53">
<Number>0001</Number>
<Articles>
<Article Id="1">
<Title>Article 1</Title>
<Size>1205</Size>
</Article>
<Article Id="2">
<Title>Article 2</Title>
<Size>817</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="54">
<Number>0002</Number>
<Articles>
<Article Id="3">
<Title>Article 3</Title>
<Size>1570</Size>
</Article>
<Article Id="4">
<Title>Article 4</Title>
<Size>2510</Size>
</Article>
</Articles>
</Magazine>
<Magazine Id="55">
<Number>0003</Number>
<Articles>
<Article Id="5">
<Title>Article 5</Title>
<Size>910</Size>
</Article>
</Articles>
</Magazine>
</Magazines>
Но, используя подзапрос, я не могу фильтровать журналы по столбцам статей (без сложных дополнительных запросов).
Режим "ДЛЯ XML AUTO" не подходит, поскольку он очень прост и не поддерживает некоторые функции "PATH" (например, атрибуты с использованием @, ROOT и т.д.)
Итак, есть ли какая-либо возможность в режиме "ПУТЬ" группировать данные внутренней таблицы, как в режиме "АВТО"?
Спасибо!
Решение
Что ж, вы можете стать на шаг ближе, указав "размер > 1000" внутри вашего подзапроса:
SELECT M.Id AS "@Id",
M.Number AS "Number",
(SELECT Articles.Id AS "@Id",
Articles.Title AS "Title",
Articles.Size AS "Size"
FROM Articles
WHERE Articles.MagazineId = M.Id
AND Articles.Size > 1000
FOR XML PATH('Article'), ROOT('Articles'), TYPE
)
FROM Magazines AS M
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
Чего вам сейчас не хватает, так это того факта, что вы по-прежнему будете получать журналы, в которых нет статей размером > 1000.Вы можете устранить их примерно так:
SELECT M.Id AS "@Id",
M.Number AS "Number",
(SELECT Articles.Id AS "@Id",
Articles.Title AS "Title",
Articles.Size AS "Size"
FROM Articles
WHERE Articles.MagazineId = M.Id
AND Articles.Size > 1000
FOR XML PATH('Article'), ROOT('Articles'), TYPE
)
FROM Magazines AS M
WHERE EXISTS(SELECT * FROM Articles
WHERE Articles.MagazineId = M.Id
AND Articles.Size > 1000)
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
(непроверенный, у меня сейчас нет SQL-сервера под рукой).
У вас это работает?Дает ли это вам журналы и статьи, которые вы ищете?
Марк
Другие советы
Используйте FOR XML в явном виде. Это самый длинный код для записи, но исчезают проблемы с производительностью, вызываемые подзапросами.