Как мне написать этот запрос более эффективно?

#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.