mysql: выберите последние 10 сообщений и для каждого сообщения последние 3 ответа - PullRequest
1 голос
/ 23 февраля 2011

Для простоты приведем таблицу сообщений к минимуму с некоторыми примерами данных

message_id  reply_to    createdate
1           0           123
2           0           124
3           0           123
4           1           154
5           1           165

reply_to - это message_id, сообщение которого является ответом на

так что я ищу sql-оператор / процедуру / функцию / другой дизайн таблицы, который позволил бы мне выбрать последние 10 сообщений и для каждого из них последние 3 ответа, я не против изменить структуру таблицы или даже сохранить какой-то вид запись за последние 3 ответа

просто выберите последние 10 сообщений

SELECT * FROM message ORDER BY createdate LIMIT 10;

и для каждого из этих сообщений ответы

SELECT * FROM message WHERE reply_to = :message_id: ORDER BY createdate LIMIT 3;

Мои попытки на данный момент:

  • тройное внешнее соединение по таблице сообщений в ответах
  • простое объединение, но mysql не допускает ограничений в объединениях
  • использование HAVING COUNT (DISTINCT reply_to) <= 3, но конечно HAVING оценивается последним </li>

я не смог получить ни одного из тех, кто работал

моя последняя опция atm - иметь отдельную таблицу для отслеживания последних 3 ответов на сообщение

message_reply: message_id, r_1, r_2, r_3

и затем обновление этой таблицы с использованием триггеров поэтому новая строка в таблице сообщений, которая является ответом, обновляет таблицу message_reply

UPDATE message_reply SET r_3 = r_2, r_2 = r_1, r_1 = NEW.reply_to WHERE message_id = NEW.message_id

тогда я мог бы просто запросить таблицу сообщений для этих записей

у кого-нибудь есть лучшее предложение или даже рабочий оператор SQL?

спасибо

EDIT:

добавлено ПОЯСНИТЬ результаты

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived4>  ALL     NULL    NULL    NULL    NULL    3    
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    10  Using where; Using join buffer
1   PRIMARY     r   eq_ref  PRIMARY,message_id,message_id_2     PRIMARY     4   func    1    
4   DERIVED     NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
5   UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
6   UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
NULL    UNION RESULT    <union4,5,6>    ALL     NULL    NULL    NULL    NULL    NULL     
2   DERIVED     m   ALL     NULL    NULL    NULL    NULL    299727   
3   DEPENDENT SUBQUERY  r   ref     reply_to,reply_to_2     reply_to_2  4   testv4.m.message_id     29973    

РЕДАКТИРОВАТЬ 2:

Ну, я попробовал метод таблицы message_reply, и это то, что я сделал

построить таблицу:

message_reply: message_id, r_1, r_2, r_3

построить триггер:

DELIMITER |
CREATE TRIGGER i_message AFTER INSERT ON message
  FOR EACH ROW BEGIN
    IF NEW.reply_to THEN
        INSERT INTO message_replies (message_id, r_1) VALUES (NEW.reply_to, NEW.message_id)
        ON DUPLICATE KEY UPDATE r_3 = r_2, r_2 = r_1, r_1 = NEW.message_id;
    ELSE
        INSERT INTO message_replies (message_id) VALUES (NEW.message_id);
    END IF;
  END;
|
DELIMITER ;

и выберите сообщения:

SELECT m.*,r1.*,r2.*,r3.* FROM message_replies mr
LEFT JOIN message m ON m.message_id = mr.message_id
LEFT JOIN message r1 ON r1.message_id = mr.r_1
LEFT JOIN message r2 ON r2.message_id = mr.r_2
LEFT JOIN message r3 ON r3.message_id = mr.r_3

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

протестировано с еще несколькими наборами вставок по 100 Кб, чтобы увидеть снижение производительности для триггера Обработка 100 тыс. строк заняла 0,4 секунды дольше, чем без Tirgger общее время для вставки составило около 12 секунд (в таблицах myIsam)

Ответы [ 3 ]

1 голос
/ 23 февраля 2011

Рабочий пример:

РЕДАКТИРОВАТЬ - (см. Редакцию для более раннего запроса)

Полное создание таблицы и план объяснения
Примечание: таблица "datetable" содержит только вседаты около 10 лет.Он используется только для генерации строк.

drop table if exists messages;
create table messages (
   message_id int primary key, reply_to int, createdate datetime, index(reply_to));

insert into messages 
select @n:=@n+1, floor((100000 - @n) / 10), a.thedate
from (select @n:=0) n
cross join datetable a
cross join datetable b
limit 1000000;

Выше генерируется 1 млн. Сообщений и несколько действительных ответов.Запрос:

select m1.message_id, m1.reply_to, m1.createdate, N.N, r.*
from
(
    select m.*, (
         select group_concat(r.message_id order by createdate)
          from messages r
        where r.reply_to = m.message_id) replies
     from messages m
     order by m.message_id
    limit 10
) m1
inner join ( # this union-all query controls how many replies per message
    select 1 N union all
     select 2 union all
     select 3) N
  on (m1.replies is null and N=1) or (N <= length(m1.replies)-length(replace(m1.replies,',','')))
left join messages r
  on r.message_id = substring_index(substring_index(m1.replies, ',', N), ',', -1)

Время: 0,078 сек

Объяснить план

id     select_type         table        type      possible_keys    key      key_len ref                rows    Extra
1      PRIMARY             <derived4>   ALL      (NULL)            (NULL)   (NULL)  (NULL)             3    
1      PRIMARY             <derived2>   ALL      (NULL)            (NULL)   (NULL)  (NULL)             10      Using where
1      PRIMARY             r            eq_ref   PRIMARY           PRIMARY  4       func               1    
4      DERIVED             (NULL)       (NULL)   (NULL)            (NULL)   (NULL)  (NULL)             (NULL)  No tables used
5      UNION               (NULL)       (NULL)   (NULL)            (NULL)   (NULL)  (NULL)             (NULL)  No tables used
6      UNION               (NULL)       (NULL)   (NULL)            (NULL)   (NULL)  (NULL)             (NULL)  No tables used
(NULL) UNION RESULT        <union4,5,6> ALL      (NULL)            (NULL)   (NULL)  (NULL)             (NULL)    
2      DERIVED             m            index    (NULL)            PRIMARY  4       (NULL)             1000301    
3      DEPENDENT SUBQUERY  r            ref      reply_to          reply_to 5       test.m.message_id  5       Using where
0 голосов
/ 23 февраля 2011

Примечание: Этот ответ предоставляет полезную информацию для сравнения комментариев OMG, поэтому, даже если его необходимо удалить, оставьте его на некоторое время.

OMG:Проверьте сопряжение тегов mysql и "great-n-per-group" - запрос очень распространен.OMG: Тогда зайдите на вопросы и вежливо сообщите, если не ответите.

Я следовал вашим инструкциям OMG, и это то, что я придумал с
https://stackoverflow.com/questions/tagged/greatest-n-per-group+mysql

  1. SQL - Дайте мне 3 совпадения только для каждого типа
  2. mySQL Возвращение 5 лучших результатов в каждой категории
  3. MySQL SELECT nзаписи основаны на GROUP BY

Возможно, вы неправильно поняли вопрос из-за 3, которые выглядели наиболее похожими на первой странице результатов (2 из которых являются моими ответами), вопросы касаютсяодно измерение (верхний n на категорию) для всей таблицы.Решения, предлагаемые неизменно row_number ALL записей в таблице, упорядоченной по категориям.

Сравните это с оптимизированным ответом, предоставленным для этого вопроса для проблемной области top-n-category -> top-m-per-category ивы поймете, что этот вопрос другой.

Нет необходимости visit the questions and courteously inform if not answer, потому что

  1. Ответ на эти вопросы действителен
  2. Ответк этому вопросу относится
0 голосов
/ 23 февраля 2011

Я бы посоветовал вам построить дополнительную таблицу и заставить ее работать с таким количеством шагов, сколько необходимо.Иногда для визуализации ответа нужны дополнительные шаги.В конце вы можете скомпилировать SQL в один вложенный оператор.

...