MySQL JOIN запросы - Система обмена сообщениями - PullRequest
2 голосов
/ 18 мая 2011

У меня есть следующие таблицы для системы обмена сообщениями, и мне было интересно, как мне поступить с запросом к БД на предмет того, сколько разговоров содержит новые сообщения.

Мои таблицы выглядят следующим образом

Conversation
------------
id
subject

Messages
--------
id
conversation_id
user_id (sender)
message
timestamp (time sent)

Participants
------------
conversation_id
user_id
last_read (time stamp of last view user viewed conversation)

Я пытаюсь выполнить следующий запрос, но он не возвращает результатов:

SELECT COUNT(m.conversation_id) AS count
FROM (messages_message m)
INNER JOIN messages_participants p ON p.conversation_id = m.conversation_id
WHERE `m`.`timestamp` > 'p.last_read'
AND `p`.`user_id` = '5'
GROUP BY m.conversation_id
LIMIT 1 

Кроме того, мне, вероятно, придется запускать это при каждой загрузке страницы - какие-нибудь советы, как сделать это как можно быстрее?

Приветствия

EDIT

У меня есть еще один несколько связанный вопрос, если кто-нибудь будет так любезен, чтобы помочь.

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

SELECT SQL_CALC_FOUND_ROWS c.*, last_msg.*, new_msgs.count as new_msgs_count 
                        FROM ( messages_conversation c ) 
                        INNER JOIN messages_participants p ON p.user_id = '5' 
                        INNER JOIN ( SELECT m.* 
                                    FROM (messages_message m) 
                                    ORDER BY m.timestamp DESC 
                                    LIMIT 1) last_msg 
                                ON c.id = last_msg.conversation_id 
                        LEFT JOIN ( SELECT COUNT(m.id) AS count, m.conversation_id, m.timestamp  
                                    FROM (messages_message m) ) new_msgs 
                                ON c.id = new_msgs.conversation_id AND new_msgs.timestamp > p.last_read 
                        LIMIT 0,10

Должен ли я определить, являются ли разговоры непрочитанными, выполнив оператор IF в MySQL, или я должен преобразовать и сравнить временные метки на PHP?

Еще раз спасибо, RS7

Ответы [ 2 ]

2 голосов
/ 18 мая 2011

'p.last_read', как указано выше, является строковой константой - удалите из нее кавычки и посмотрите, что это изменит, RS7. Если user_id является целым числом, удаляйте также кавычки из '5'.

Что касается производительности, убедитесь, что у вас есть индексы для всех соответствующих столбцов. messages_participants.user_id и messages_message.timestamp являются двумя важными столбцами для индексации.

1 голос
/ 18 мая 2011

Да, у вас есть проблема в вашем запросе.

Во-первых, вы должны были заметить, что подсчитываете столбец, который вы группируете, поэтому результат подсчета будет равен 1. Во-вторых, вы сравниваете метку времени сстрока: m.timestamp > 'p.last_read'.Наконец, избегайте использования LIMIT, когда вы знаете, что ваш запрос вернет одну строку (будьте уверены: p).

Попробуйте:

SELECT
  COUNT(m.conversation_id) AS count
FROM
  messages_message m
INNER JOIN
  messages_participants p ON p.conversation_id = m.conversation_id
WHERE
  m.timestamp > p.last_read
  AND p.user_id = 5

, если вы хотите увеличить количество выполняемых запросоввремя, когда вы можете создать новый индекс в message_participants (journal_id, user_id), чтобы проиндексировать разговоры по пользователям, а затем изменить ваш запрос с помощью:

SELECT
  COUNT(m.conversation_id) AS count
FROM
  messages_message m
INNER JOIN
  messages_participants p ON p.conversation_id = m.conversation_id AND p.user_id = 5
WHERE
  m.timestamp > p.last_read

, чтобы ваш движок БД теперь мог фильтровать JOIN, просто посмотрев натаблица индексов.Вы можете углубиться в эту мысль, индексировав также временную метку: (timestamp, dialog_id, user_id) и поместите условие where в условие соединения.

Что бы вы ни выбрали, всегда сначала ставьте наиболее селективное поле, чтобы увеличитьселективность.

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

Сначала прокомментируем ваш запрос:

SELECT
  SQL_CALC_FOUND_ROWS c.*,
  last_msg.*,
  new_msgs.count as new_msgs_count
FROM
  messages_conversation c
INNER JOIN
  messages_participants p ON p.user_id = 5 -- Join with every conversations of user 5; if id is an integer, avoid writing '5' (string converted to an integer).
INNER JOIN
( -- Select every message : you could already select here messages from user 5
  SELECT
    * 
  FROM
    messages_message m
  ORDER BY -- this is not the goal of ORDER BY. Use MAX to obtain to latest timestamp.
    m.timestamp DESC 
  LIMIT 1
) last_msg ON c.id = last_msg.conversation_id  -- this query return one row and you want to have the latest timestamp for each conversation.
LEFT JOIN
(
  SELECT
    COUNT(m.id) AS count,
    m.conversation_id,
    m.timestamp  
  FROM
    messages_message m
) new_msgs ON c.id = new_msgs.conversation_id AND new_msgs.timestamp > p.last_read 
LIMIT 0,10

Перефразируем ваш запрос: выберите количество новых сообщений в теме разговора, его последнее сообщениеи отметка времени для пользователя @ id.

Делайте это шаг за шагом:

Выбор последнего сообщения, отметки времени в разговоре для каждого пользователя:

SELECT -- select the latest timestamp with its message
  max(timestamp),
  message
FROM
  messages_message
GROUP BY
  user_id

Агрегирует функции (MAX,MIN, SUM, ...) работают в текущей группе.Прочитайте это как «для каждой группы вычислите агрегатные функции, затем выберите то, что мне нужно, если мои условия выполняются».Так что это приведет к одному ряду на группу.Таким образом, этот последний запрос выбирает последнее сообщение и метку времени каждого пользователя в таблице messages_message.Как видите, это значение легко выбрать для конкретного пользователя, добавив предложение WHERE:

SELECT
  MAX(timestamp),
  message
FROM
  messages_message
WHERE
  user_id = @id
GROUP BY
  user_id

Количество сообщений на разговор: для каждого разговора подсчитайте количество сообщений

SELECT
  COUNT(m.id) -- assuming id column is unique, otherwise count distinct value.
FROM
  messages_conversation c
INNER JOIN -- The current user participated to the conversation
  messages_participant p ON p.conversation_id = c.id AND p.user_id = @id
OUTER JOIN -- Messages of the conversation where the current user participated, newer than last read its time
  messages_message m ON m.conversation_id = c.id AND m.timestamp > p.last_read = @id
GROUP BY
  c.id -- for each conversation

INNER JOIN не возвращает строки для разговоров, в которых текущий пользователь не участвовал.Затем OUTER JOIN объединится с NULL столбцами, если условие ложно, поэтому COUNT вернет 0 - новых сообщений нет.

Собираем все вместе.

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

SELECT
   last_msg.conversation_id,
   last_msg.message,
   last_msg.max_timestamp,
   new_msgs.nb
FROM
(
  SELECT
    MAX(timestamp)   AS max_timestamp,
    message,
    conversation_id
  FROM
    messages_message
  WHERE
    user_id = @id
  GROUP BY
    user_id
) last_msg
JOIN
(
  SELECT
    c.id         AS conversation_id
    COUNT(m.id)  AS nb
  FROM
    messages_conversation c
  INNER JOIN
    messages_participant p ON p.conversation_id = c.id AND p.user_id = @id
  OUTER JOIN
    messages_message m ON m.conversation_id = c.id AND m.timestamp > p.last_read = @id
  GROUP BY
    C.id
) new_msgs ON new_msgs.conversation_id = last_msg.conversation_id
-- put here and only here a order by if necessary :)
...