2010-06-30 2 views
5

У меня есть простая таблица, как это:Выбор всех детей от всех родителей в MySql

+------------+---------+-----------+--------------+ 
| comment_id | post_id | parent_id | comment_text | 
+------------+---------+-----------+--------------+ 
|   1 |  200 |   0 |   a | 
|   2 |  200 |   0 |   b | 
|   3 |  200 |   1 |   c | 
|   4 |  200 |   1 |   d | 
|   5 |  200 |   0 |   e | 
|   6 |  200 |   2 |   f | 
|   7 |  200 |   2 |   g | 
|   8 |  200 |   0 |   h | 
|   9 |  200 |   0 |   i | 
|   10 |  200 |   1 |   k | 
+------------+---------+-----------+--------------+ 
  • Колонка parent_id говорит нам, что этот комментарий является ответом на другой комментарий с этим идентификатором.

  • Предположим, что комментариев есть только один уровень.

Теперь мне нужно вернуться только первые 5 родительских замечаний и всех дети, которые принадлежат им.

У меня есть запрос для этого, но есть одна проблема.

(
    SELECT c.* 
    FROM comments AS c 
    WHERE c.post_id = '200' AND parent_id='0' 
    LIMIT 0,5 
    ) 
    UNION 
    (
    SELECT c.* 
    FROM comments AS c 
    WHERE c.post_id = '200' AND c.parent_id IN 
    (
     SELECT c.comment_id 
     FROM comments AS c 
     WHERE c.post_id= '200' AND parent_id='0' 
     LIMIT 0,5 
    ) 
    ) 

Проблема в том, что моя текущая версия mySQL не поддерживает LIMIT в подзапросах. Также кажется, что я выполняю тот же запрос SELECT дважды!

Я уверен, что должен быть способ сделать это более элегантно.

+0

Какую версию MySQL вы используете? 'SELECT @@ version'. –

+0

это MySQL 5.1.36-community-log –

+0

Почему у вас есть два комментария с одним и тем же идентификатором? Это ошибка в вопросе? –

ответ

0

Можете ли вы просто сделать 2 выбора?

Select * from comments where parentID = 0 limit 5 

select * from comments where parentID IN (*list of the 5 parentIDs) 
+0

Я хочу выполнить все в одном запросе, чтобы у меня был один результирующий набор. Я не хочу передавать * список из 5 parentIDs –

+2

да, но * why *. – Zak

0

На моей версии MySQL, вы можете использовать ограничение в подзапросов:

select * 
from (
     select * 
     from comments c 
     where c.post_id = '200' 
       and c.parent_id = 0 
     limit 0, 3 
     ) s 
union 
select c.* 
from (
     select * 
     from comments c 
     where c.post_id = '200' 
       and c.parent_id = 0 
     limit 0, 3 
     ) p 
join comments c 
on  c.parent_id = p.comment_id; 

Дубликат выбрать трудно избежать без with заявления, что MySQL не поддерживает. Вы можете создать временную таблицу для их хранения:

drop table if exists top5comments; 

create table top5comments 
    select * 
    from comments c 
    where c.post_id = '200' 
      and c.parent_id = 0 
    limit 0, 3; 

insert top5comments 
select children.* 
from top5comments parents 
join comments children 
on  children.parent_id = parents.comment_id; 

select * from top5comments; 

Это потребует изменений, если запрос может выполняться несколькими соединениями одновременно.

P.S. Кстати, заявление limit обычно сопровождается предложением order by. Без order by вы получите произвольные строки, и результаты могут отличаться от запроса к запросу.

1

В конце концов, я в конечном итоге, используя что-то вроде того, что предложил Andomar но без подзапроса:

 (
     select * 
     from comments c 
     where c.post_id = '200' 
       and c.parent_id = 0 
     order by comment_id DESC limit 0, 4 
     ) 
union 
     (
     select * 
     from comments c 
     join comments p 
     on  c.parent_id = p.comment_id 
     where c.post_id = '200' 
       and c.parent_id != '0' 
     ) 
     order by comment_id DESC 

EDIT: Как было отмечено правильно niaher этот запрос возвратил гораздо больший набор результатов, чем требуется. Это делает это решение неправильным.

К счастью, я нашел другой способ сделать это, гораздо более элегантным и чистым.

Правильный способ сделать это:

SELECT c.* 
    FROM (
     SELECT comment_id 
     FROM comments 
     WHERE post_id = 200 AND parent_id = 0 
     ORDER BY comment_id ASC 
     LIMIT 0,5  
    ) AS q 
JOIN comments AS c 
    ON c.comment_id = q.comment_id OR c.parent_id = q.comment_id 

Я проверил этот запрос, и он работает гораздо быстрее, чем предыдущий.

+1

Я не думаю, что это решение удовлетворяет вашему требованию: «получить первые 5 родительских комментариев и все ответы на них». Вместо этого этот код «извлекает первые 5 родительских комментариев и все ответы». Проблема заключается в том, что «все ответы детали». Ваш набор результатов потенциально намного больше, чем вы действительно нуждаетесь. Также не похоже, что вам нужно соединение во втором подзапросе! – niaher

+0

И да, я на 2 года опаздываю на вечеринку! – niaher

1

Это самый элегантный способ сделать это: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/. Одна проблема, с которой вы столкнетесь, заключается в том, что вставки потребуется некоторое время, так как другие строки должны быть обновлены; однако этого можно избежать с помощью некоторого творчества.Эта статья - отличное начало.

+0

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

0

Может быть, вы имеете в виду это:

SELECT 
    *, 
    (CASE WHEN parent_id = 0 THEN comment_id ELSE parent_id END) AS use_id 
FROM 
    comments 
WHERE 
    (CASE WHEN parent_id = 0 THEN comment_id ELSE parent_id END) IN (
    SELECT comment_id 
    FROM  comments 
    WHERE  parent_id = 0 
    ORDER BY comment_id ASC 
    LIMIT  3) 
ORDER BY 
    use_id ASC, 
    parent_id; 

Что она делает это взять три «первый» (по заказу comment_id ASC) родительских замечаний и все свои ребенок, а затем отсортировать весь список по родительскому,/comment_id (сначала родитель, потом дети).

СЛУЧАЙ, КОГДА ЭТО ЭТО ЗАВЕРШЕНО для SQLite (так я тестирую такие штучки).

- редактировать

Если эти записи существуют (4 больше, чем ваши 10):

comment_id post_id parent_id comment 
1   200  0   a 
2   200  0 b 
3   200  1 c 
4   200  1 d 
5   200  0 e 
6   200  2 f 
7   200  2 g 
8   200  0 h 
9   200  0 i 
10   200  1 j 
11   200  1 k 
12   200  2 l 
13   200  0 m 
14   200  1 n 

это будет результат:

comment_id post_id parent_id comment use_id 
1  200 0 a 1 
3  200 1 c 1 
4  200 1 d 1 
10  200 1 j 1 
11  200 1 k 1 
14  200 1 n 1 
2  200 0 b 2 
6  200 2 f 2 
7  200 2 g 2 
12  200 2 l 2 
5  200 0 e 5 
Смежные вопросы