#sql-server #performance
#sql-сервер #Производительность
Вопрос:
На моем профильном веб-сайте пользователи могут комментировать и комментировать комментарий (1 уровень, как facebook имеет). Я реализую некоторую разбивку на страницы, потому что в 1 профиле могут быть тысячи комментариев. Разбивка на страницы работает, но, поскольку есть дочерние комментарии, простой запрос top n прерывает диалог. Я хочу разбивать на страницы только родительские комментарии, а не дочерние.
Таблица ‘comments’, которая имеет:
- commentID
- commentText
- parentCommentID
- commentOnUserID
Проблема здесь в том, что я хочу разбивать на страницы только комментарии, которые являются родительскими (parentCommentID = 0). Итак, я пишу запрос типа:
select * from Comments c
where c.parentCommentID = 0
and c.commentOnUserID = 65939
(Я опустил фактический запрос на разбивку на страницы, потому что он не относится к делу)
но я также хочу загрузить дочерние элементы всех этих комментариев, дочерний элемент также является комментарием, но затем с parentCommentID = some commentId:
select * from comments c
where c.parentCommentID in ( * get the commentId's from the previous query)
and c.commentOnUserID = 65939
Есть ли способ эффективно объединить два в одном запросе?
Ответ №1:
declare @T table(commentID int,
commentText varchar(max),
parentCommentID int,
commentOnUserID int)
insert into @T values
(1, 'Comment 1', 0, 1),
(2, 'Comment 2', 0, 1),
(3, 'Comment 3', 0, 1),
(4, 'Comment 4 sub 1', 1, 1),
(5, 'Comment 5 sub 1', 1, 1),
(6, 'Comment 6 sub 1', 1, 1),
(7, 'Comment 1 sub 2', 2, 1),
(8, 'Comment 1 sub 2', 2, 1),
(9, 'Comment 1 sub 3', 3, 1)
declare @UserID int = 1
;with cte as
(
select
T.commentID,
T.CommentText,
row_number() over(order by commentID) as rn
from @T as T
where
T.parentCommentID = 0 and
T.commentOnUserID = @UserID
union all
select
T.commentID,
T.CommentText,
C.rn
from @T as T
inner join cte as C
on T.parentCommentID = C.commentID
)
select *
from cte
where rn between 1 and 2 -- use rn for pagination
order by rn, commentID
Результат
commentID parentCommentID CommentText rn
----------- --------------- -------------------- --------------------
1 0 Comment 1 1
4 1 Comment 4 sub 1 1
5 1 Comment 5 sub 1 1
6 1 Comment 6 sub 1 1
2 0 Comment 2 2
7 2 Comment 1 sub 2 2
8 2 Comment 1 sub 2 2
Ответ №2:
Что-то вроде этого:
WITH
ParentComment AS (
SELECT * from Comments c
WHERE c.parentCommentID = 0
AND c.commentOnUserID = 65939
)
SELECT *
FROM Comments c
WHERE c.commentOnUserID = 65939
AND (
c.CommentID IN (SELECT CommentID FROM ParentComment)
OR c.ParentCommentID IN (SELECT CommentID FROM ParentComment)
)
Не тестировал синтаксис в SQL Server, но это должно быть общей идеей.
Ответ №3:
Я бы сделал это примерно следующим образом:
SELECT p.*, c.*
FROM comment c LEFT JOIN comment p ON (c.parentCommentID = p.commentID)
WHERE p.parentCommentID = 0
AND p.commentOnUserID = 65939
Я уверен, что есть какой-то способ красиво включить родительский элемент в дочерние результаты, но я знаю, что, по крайней мере, в MySQL есть проблемы с производительностью при размещении OR в условии join-on.