Получение самого последнего сообщения в ветке - PullRequest
12 голосов
/ 07 июня 2011

У меня есть запрос, который получает всю информацию, необходимую для главной страницы системы обмена сообщениями (включая количество непрочитанных сообщений и т. Д.) ... но в настоящее время он получает сообщение original threads. Я хотел бы дополнить приведенный ниже запрос, чтобы вместо него получить самое большее последнее сообщение в каждой теме.

Этот запрос очень близок, однако мои посредственные навыки SQL не позволяют мне закончить ...

$messages = array();
$unread_messages_total = 0;

$messages_query = "
SELECT m.*
    , COUNT(r.id) AS num_replies
    , MAX(r.datetime) AS reply_datetime
    , (m.archived NOT LIKE '%,".$cms_user['id'].",%') AS message_archive
    , (m.viewed LIKE '%,".$cms_user['id'].",%') AS message_viewed 
    , SUM(r.viewed NOT LIKE '%,".$cms_user['id'].",%') AS unread_replies 
    , CASE
        WHEN MAX(r.datetime) >= m.datetime THEN MAX(r.datetime)
        ELSE m.datetime
        END AS last_datetime
FROM directus_messages AS m
LEFT JOIN directus_messages as r ON m.id = r.reply
WHERE m.active = '1'  
AND (m.to LIKE '%,".$cms_user['id'].",%' OR m.to = 'all' OR m.from = '".$cms_user['id']."') 
GROUP BY m.id
HAVING m.reply = '0' 
ORDER BY last_datetime DESC";

foreach($dbh->query($messages_query) as $row_messages){
    $messages[] = $row_messages;
    $unread_messages_total += (strpos($row_messages['archived'], ','.$cms_user['id'].',') === false && ( (strpos($row_messages['viewed'], ','.$cms_user['id'].',') === false && $row_messages['unread_replies'] == NULL) || ($row_messages['unread_replies']>0 && $row_messages['unread_replies'] != NULL) ) )? 1 : 0;
}

Заранее благодарим за любую помощь, которую вы можете оказать!

РЕДАКТИРОВАТЬ: (база данных)

CREATE TABLE `cms_messages` (
  `id` int(10) NOT NULL auto_increment,
  `active` tinyint(1) NOT NULL default '1',
  `subject` varchar(255) NOT NULL default '',
  `message` text NOT NULL,
  `datetime` datetime NOT NULL default '0000-00-00 00:00:00',
  `reply` int(10) NOT NULL default '0',
  `from` int(10) NOT NULL default '0',
  `to` varchar(255) NOT NULL default '',
  `viewed` varchar(255) NOT NULL default ',',
  `archived` varchar(255) NOT NULL default ',',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

РЕДАКТИРОВАТЬ 2: (Требования)

  • Возвращает все родительские сообщения для определенного user_id: $cms_user['id']
  • Возвращает количество ответов для этого родительского сообщения: num_replies
  • Возвращает количество непрочитанных ответов на это родительское сообщение: unread_replies
  • Возвращает дату родительского сообщения или его самого последнего ответа: last_datetime
  • Возвращает ли сообщение в архиве: message_archive
  • Возвращает ли сообщение было просмотрено: message_viewed
  • Возвращать все сообщения в порядке даты и времени DESC
  • Вернуть новейшую message от родителя или ответы, если есть какие-то (например, gmail)

Ответы [ 3 ]

5 голосов
/ 29 июня 2011

Если у вас есть только 2 уровня сообщений (т.е. только родительские сообщения и прямые ответы), вы можете попробовать этот запрос:

select
    root_message.id,
    root_message.active,
    root_message.subject,
    case
        when max_reply_id.max_id is null then 
            root_message.message
        else
            reply_message.message
    end as message,
    root_message.datetime,
    root_message.reply,
    root_message.from,
    root_message.to,
    root_message.viewed,
    root_message.archived
from
    -- basic data
    cms_messages as root_message
    -- ID of last reply for every root message
    left join (
        select 
            max(id) as max_id, 
            reply as parent_id 
        from 
            cms_messages
        where
            reply <> 0 
        group by 
            reply
    ) as max_reply_id on max_reply_id.parent_id = root_message.id                                              
    left join cms_messages as reply_message on reply_message.id = max_reply_id.max_id
where
    root_message.reply = 0

Он использует подзапрос max_reply_id в качестве источника данных для выбора идентификаторапоследнего ответа.Если он существует (то есть, если есть ответы), используется reply_message.message.Если он не существует (ответ на корневое сообщение не найден), то используется root_message.message.

Также следует подумать о структуре таблицы.Например, было бы более разумно, если бы reply содержал либо NULL, если это родительское сообщение, либо идентификатор существующего сообщения.В настоящее время вы устанавливаете 0 (идентификатор несуществующего сообщения), что неверно.Типы viewed и archived также странные.

Редактировать: вам также следует избегать использования предложения having.Вместо этого используйте where, когда это возможно.


Вот новый запрос, который должен соответствовать вашим требованиям.Если с ним возникнут какие-либо проблемы (т. Е. Если он возвращает неправильные данные), дайте мне знать.

Как и первый запрос, он:

  • использует подзапрос reply_summary для накопленияданные об ответах (идентификатор последнего ответа, количество ответов и количество непрочитанных ответов);
  • присоединяет этот подзапрос к базовой таблице;
  • присоединяет cms_messages as reply_message к подзапросу на основе reply_summary.max_reply_id, чтобы получить данные о последнем ответе (сообщение, дата и время).

Я упростил способ определения last_datetime - теперь требуется либо время последнего ответа (если естьлюбой ответ) или время исходного сообщения (когда ответы не найдены).

Я не отфильтровал ответы по полям from и to.Если необходимо, следует обновить where предложение подзапроса reply_summary.

select
    parent_message.id,
    parent_message.subject,
    parent_message.message,
    parent_message.from,
    parent_message.to,
    coalesce(reply_summary.num_replies, 0) as num_replies,
    last_reply_message.datetime as reply_datetime,
    (parent_message.archived NOT LIKE '%,{$cms_user['id']},%') AS message_archive,
    (parent_message.viewed   LIKE     '%,{$cms_user['id']},%') AS message_viewed,
    reply_summary.unread_replies,
    coalesce(last_reply_message.message, parent_message.message) as last_message,
    coalesce(last_reply_message.datetime, parent_message.datetime) as last_datetime
from
    cms_messages as parent_message
    left join (
        select
            reply as parent_id,
            max(id) as last_reply_id,
            count(*) as num_replies,
            sum(viewed not like '%,{$cms_user['id']},%') as unread_replies
        from
            cms_messages
        where
            reply <> 0 and
            active = 1
        group by
            reply
    ) as reply_summary on reply_summary.parent_id = parent_message.id
    left join cms_messages as last_reply_message on last_reply_message.id = reply_summary.last_reply_id
where
    parent_message.reply = 0 and
    parent_message.active = 1 and
    (parent_message.to like '%,{$cms_user['id']},%' or parent_message.to = 'all' or parent_message.from = '{$cms_user['id']}')
order by
    last_datetime desc;
3 голосов
/ 04 июля 2011

ваша проблема в том, что вы выбираете только m записей независимо от порядка записей r.

попробуйте добавить

SELECT m.*, r.*

или

SELECT r.*, m.*

если вы используете PDO :: FETCH_ASSOC в качестве режима извлечения PDO (при условии, что вы используете PDO для доступа к вашей базе данных), результатом будет ассоциативный массив, в котором, если набор результатов содержит несколько столбцов с одинаковым именем, PDO :: FETCH_ASSOC возвращаеттолько одно значение на имя столбца.не уверен, какой порядок имеет приоритет, поэтому вам придется попробовать оба варианта.

, если столбцы определены в правильном порядке, они вернут значение r. *, если оно существует, или значение m. *, еслиникаких записей не существует.Имеет ли это смысл?таким образом, ваш набор результатов будет содержать самую последнюю запись независимо от того, в какой таблице (m или r) они содержатся.

http://www.php.net/manual/en/pdo.constants.php

2 голосов
/ 29 июня 2011

Боюсь, что вы не сможете решить эту проблему с помощью одного запроса.Либо вам придется использовать больше запросов и собирать информацию в окружающем коде, либо вам придется перестроить структуру базы данных для вашей системы обмена сообщениями (таблицы: темы, сообщения и т. Д.).Если вы решите изменить структуру базы данных, вам также следует позаботиться о том, как вы обрабатываете поля viewed и archived.То, как вы используете эти поля (только varchar 255!), Может работать для некоторых пользователей, но как только появится больше пользователей с более высокими идентификаторами, ваша система сообщений выйдет из строя.

...