2011-12-23 2 views
3

Я создаю форум, поэтому создал стол с сообщениями. Одним из полей является Body с типом XML. Теперь я хотел бы создать запрос, который возвращает все сообщения и количество детей каждого сообщения. Я делаю это с помощью агрегатной функции. Мне нужно использовать группу, когда я использую функцию агрегата. Когда я использую поле в group by, я получаю следующее исключение: тип данныхКак добавить тип данных XML в предложение GROUP BY?

XML, нельзя сравнивать или сортирует, за исключением того, при использовании IS NULL оператора.

Как это можно решить?

Мой запрос:

SELECT 
    Post.PostId, Post.[Body], Count(Children.PostId) 
FROM 
    dbo.Post Post, 
    dbo.Post Children 
WHERE 
    Children.ParentId = Post.PostId 
GROUP BY 
    Post.PostId, 
    Post.[Body] 

ответ

5

Вы можете сделать агрегацию в КТР затем присоединиться на что

WITH Children(Cnt, ParentId) 
    AS (SELECT COUNT(*), 
       ParentId 
     FROM dbo.Post 
     GROUP BY ParentId) 
SELECT P.PostId, 
     P.[Body], 
     ISNULL(Cnt, 0) AS Cnt 
FROM dbo.Post P 
     LEFT JOIN Children /*To include childless posts*/ 
     ON Children.ParentId = P.PostId 
ORDER BY P.PostId 
+0

Будет ли это обрезать XML, если он длиннее 8000 байт? Или это теперь не проблема в новых версиях SQL Server? –

+0

Я добавил запрос. Производительность очень важна, поэтому я не уверен, что кастинг - это правильный путь. –

+1

@MattFellows - Нет 'varchar (max)' позволяет до 2 ГБ, так же как и XML, хотя на самом деле я, вероятно, должен был сказать «nvarchar (max)» вместо этого, но в любом случае опубликовал лучшее предложение, теперь мы видели запрос. –

0

Вы можете сделать группу без выбора из поля XML в подзапроса , затем выберите поле XML и другие столбцы, соединяющие ваш подзапрос?

Используя ваш запрос, вы получите: , например.

SELECT Post.[Body], sq.* FROM 
(
    SELECT Post.PostId, Count(Children.PostId) 
    FROM 
     dbo.Post Post, 
     dbo.Post Children 
    WHERE 
     Children.ParentId = Post.PostId 
    GROUP BY 
     Post.PostId 
) as sq 
INNER JOIN Post on Post.PostId= sq.PostId 

Однако вы используете здесь соединение cross join/ansi, что не является лучшим способом сделать это. Лучше всего было бы:

SELECT Post.[Body], sq.* FROM 
(
    SELECT Post.PostId, Count(Children.PostId) 
    FROM 
     dbo.Post Post LEFT OUTER JOIN Children on Children.ParentId = Post.PostId 
    GROUP BY 
     Post.PostId 
) as sq 
INNER JOIN Post on Post.PostId= sq.PostId 
+0

Я добавил запрос. Как я могу это достичь? –

+0

Ответ обновлен - вместе с рекомендацией о присоединении Cross Join/ANSI –

+1

Эта информация дважды запрашивается в таблице «Почта». Требуется только один раз. –

0

попробовать этот способ надеюсь, что это поможет вам

SELECT Post.PostId, Post.[Body], Count(Children.PostId) over() as totalids 
    FROM 
     dbo.Post Post, 
     dbo.Post Children 
    WHERE 
     Children.ParentId = Post.PostId 

, как я работал CHECK THIS

create table xmltable (attr1 int identity ,attr2 varchar(50),attr3 xml) 

    insert into xmltable select 'i',(SELECT (SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML')) 
    union all 
    select 'j',(SELECT (SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML')) 
    union all 
    select 'k',(SELECT (SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML')) 
    union all 
    select 'l',(SELECT (SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML')) 


create table xmlid (x int ,y varchar(50)) 
    insert into xmlid select 1,'x' 
    union all 
    select 1,'y' 
    union all 
    select 2,'z' 
    union all 
    select 2,'xyz1' 
    union all 
    select 3,'xyz2' 
    union all 
    select 1,'xyz3' 
    select * from xmltable 
    select * from xmlid 

создания таблиц затем

select * from xmltable 
select * from xmlid 
select attr1,attr2, attr3,count(attr1) over(partition by attr2) as total_qtyover from xmltable a,xmlid b 
    where a.attr1=b.x 

также см. Это How to aggregrate without using `GROUP BY`?

+1

Похоже, что летать из Амстердама в Париж, пролетев над Иокогамой ... похоже, что это вызовет много накладных расходов. –

+0

@ KeesC Bakker попробуйте. – Tamkeen

+0

Ах, новый код. Это не работает. Все 'Totalids'' 1'. –

0

Я бы разложил его на два запроса, один, чтобы получить идентификаторы и подсчеты детей, и отдельный, чтобы получить тела. Выполнение GROUP BY требует, чтобы сервер сортировал по полям группировки, поэтому вы потенциально перемещаете и сравниваете множество данных, когда вам это не нужно (если у вас нет нескольких тел сообщений с одинаковым идентификатором).

+0

Можете ли вы привести пример, основанный на заданном запросе? –

+0

Запрос тот же, просто удалите ссылки на тело сообщения. Затем, когда вам нужно тело, просто сделайте отдельный «SELECT», используя идентификатор нужного сообщения. Если вы просматриваете результаты, вы можете сделать «SELECT Post». [Body] FROM dbo.Post WHERE PostId IN (post-id-list) 'где' post-id-list' - это список идентификаторов сообщений на текущую страницу. – TMN

Смежные вопросы