Используйте XMLFOREST вместо XMLELEMENT, если существует вероятность того, что столбец может иметь значение NULL, и вам не нужен пустой тег элемента, когда это происходит. Так что для тем вы бы заменили функцию XMLELEMENT на
XMLFOREST( Topic.value AS "topic" )
Существует проблема с тем, как вы включили две функции XMLAGG в один оператор SELECT. Если в вашем выражении есть только один XMLAGG, проблем нет, так как GROUP BY в родительском ключе аккуратно свернет дочерние записи, указанные в XMLAGG. Однако, когда вы указываете более одной функции XMLAGG в одном и том же SELECT, запрос создает декартово произведение внутри, поэтому в этом случае вы увидите повторяющиеся элементы внутри каждой группы, возвращаемой XMLAGG. Пример, который вы привели с тем, что для проекта было только ноль или одна тема, не демонстрирует этой проблемы, но если в проекте есть две темы и три тега, вы увидите, что каждая тема повторяется три раза, а каждый тег повторяется дважды. Чтобы предотвратить это, вам нужно переместить каждый XMLAGG в подзапрос или общее табличное выражение, которое создает один фрагмент XML, чтобы вы могли безопасно ссылаться на него из основного запроса.
Ниже приведен пример, который толкает XMLAGG в общие табличные выражения. Это также избавляет от необходимости XMLFOREST, так как XMLAGG не даст никаких результатов для пустого набора ввода.
WITH
topicxml( projectid, xmlfragment ) AS (
SELECT topic.projectid,
XMLAGG( XMLELEMENT( NAME "topic", topic.value ) ORDER BY topic.value)
FROM mydb.topic topic
GROUP BY topic.projectid
),
tagxml ( projectid, xmlfragment ) AS (
SELECT projectid,
XMLAGG( XMLELEMENT( NAME "tag", tag.value ) ORDER BY tag.value)
FROM mydb.tag tag
GROUP BY tag.projectid
)
SELECT XMLSERIALIZE ( CONTENT XMLELEMENT( NAME "project",
XMLATTRIBUTES( p.id AS "id" ),
XMLELEMENT( NAME "title", p.title ),
XMLELEMENT( NAME "description", p.description ),
XMLCONCAT( topicxml.xmlfragment, tagxml.xmlfragment )
) AS VARCHAR(2000) )
FROM mydb.project p
LEFT OUTER JOIN topicxml ON topicxml.projectid = p.id
LEFT OUTER JOIN tagxml ON tagxml.projectid = p.id
;