#mysql #sql #inner-join
#mysql #sql #внутреннее соединение
Вопрос:
У меня есть таблица last_msg
, в которой я сохраняю последнее сообщение из приватного чата между двумя пользователями, и я обновляю столбец from
и to
отправляю новое сообщение. Я использую эту таблицу, чтобы показать список пользователей, таких как facebook. Я также использую эту таблицу для других целей, поэтому я бы предпочел решить проблему, описанную ниже.
Из ON users.user_id = last_msg.from
-за того, что я получаю данные только от того, кто отправляет mensage, это было лучшее, что я получил… Это мой текущий sql:
SELECT `last_msg`.`msg`, `last_msg`.`from`, `users`.`username`, `users`.`avatar`
FROM `last_msg`
INNER JOIN `users`
ON `users`.`user_id` = `last_msg`.`from`
WHERE `last_msg`.`to` = :user_id_logged OR `last_msg`.`from` = :user_id_logged_2
В INNER JOIN users
я хочу получать данные только от другого пользователя, с которым я разговариваю в чате, и данные last_msg
могут быть как от отправителя, так и от получателя, как это делает facebook.
Итак, я попытался:
SELECT `last_msg`.`msg`, `last_msg`.`from`, `users`.`username`, `users`.`avatar`
FROM `last_msg`
INNER JOIN `users`
ON `users`.`user_id` != :user_logged
WHERE (`last_msg`.`to` = :user_logged_2 OR `last_msg`.`from` = :user_logged_3)
Но это не сработало, он возвращает список всех пользователей в таблице users
. Любые предложения о том, как я могу это исправить?
Комментарии:
1. MySQL немного устарел, но я думаю, вы хотите выбрать в таблице user, а затем объединить ее с таблицей last message.
2. можете ли вы поместить скриншоты данных таблицы, а также ожидаемый результат.
3. На заметку: я бы избегал имен, которые являются ключевыми словами SQL, такими как
from
. Это заставляет вас всегда добавлять уродливые обратные ссылки к вашим столбцам. Более того, что такоеfrom
? Имя? Идентификатор? Лучшими именами были быto_user_id
илиsender_user_id
илиuser_id_sender
или тому подобное, показывая, что это идентификатор пользователя, ссылающийся на строку в таблице users.
Ответ №1:
Вы можете попробовать присоединиться к таблице users два раза, один для пользователя from, а другой для пользователя to, как показано ниже, также обратите внимание, что вам нужно использовать LEFT Join, чтобы получить все от и до пользователей.
SELECT `last_msg`.`msg`, `last_msg`.`from`, `FromUser`.`username`, `FromUser`.`avatar`,`ToUser`.`username`,`ToUser`.`avatar`
FROM `last_msg`
LEFT JOIN `users` as `FromUser`
ON `FromUser`.`user_id` = `last_msg`.`from`
LEFT JOIN `users` as `ToUser`
ON `ToUser`.`user_id` = `last_msg`.`to`
WHERE `last_msg`.`to` = :user_id_logged OR `last_msg`.`from` = :user_id_logged_2";
Обновленный код
SELECT `last_msg`.`msg`, `last_msg`.`from`, `users`.`username`, `users`.`avatar`
FROM `last_msg`
INNER JOIN `users`
ON `users`.`user_id` =`last_msg`.`to`
WHERE `last_msg`.`from`= :user_logged
UNION
SELECT `last_msg`.`msg`, `last_msg`.`to`, `users`.`username`, `users`.`avatar`
FROM `last_msg`
INNER JOIN `users`
ON `users`.`user_id` =`last_msg`.`from`
WHERE `last_msg`.`to`= :user_logged
Комментарии:
1. этот запрос работает так же, как и мой первый фрагмент, если я изменю
ON users.user_id = last_msg.from
наON users.user_id = last_msg.to
2. Привет, можете ли вы обобщить, что вам нужно в качестве вывода, если зарегистрированный пользователь совпадает с тем, что вы хотите от пользователя? и когда зарегистрированный пользователь не является пользователем from, тогда чего вы хотите?
3. Лучше, если вы можете предоставить некоторые образцы данных и желаемый результат
4. В качестве примера. на facebook, на изображении слева, вы можете увидеть список пользователей, аватар и имя пользователя, которые отображаются, всегда принадлежат другому пользователю, но имя пользователя может быть как от отправителя, так и от получателя. У меня есть эти данные в двух разных таблицах,
last_msg
, в этой таблице я сохраняюmsg
,to
,from
, а в таблицеusers
я сохраняюusername
иavatar
, в таблицеusers
я хочу получить толькоavatar
иusername
, который не равен зарегистрированному пользователю. Вы это поняли?5. Поправьте меня, если я ошибаюсь, так что вам нужно, чтобы для данного пользователя (зарегистрированного пользователя) вы хотели, чтобы все другие пользователи, с которыми он общался?
Ответ №2:
Вот как получить сообщения, отправленные вам в последний раз:
SELECT msg, `from` as other_user_id
FROM last_msg
WHERE `to` = :user_logged;
Если вам также нужны сообщения, которые вы отправляли в последний раз:
SELECT msg, `to` as other_user_id
FROM last_msg
WHERE `from` = :user_logged;
Вы говорите, что сохраняете только одно последнее сообщение для каждого чата. Я прочитал это, поскольку для пользователей A и B в таблице будет только одна строка (либо последнее сообщение A, отправленное B, либо последнее сообщение B, отправленное A). Таким образом, вы можете просто объединить два запроса и по-прежнему показывать только одно последнее сообщение в чате.
SELECT m.msg, u.user_id, u.username, u.avatar
FROM
(
SELECT msg, `from` as other_user_id
FROM last_msg
WHERE `to` = :user_logged
UNION ALL
SELECT msg, `to` as other_user_id
FROM last_msg
WHERE `from` = :user_logged
) m
JOIN users u ON u.user_id = m.other_user_id;
Альтернативой UNION ALL
является объединение и CASE WHEN
:
SELECT m.msg, u.user_id, u.username, u.avatar
FROM last_msg m
JOIN users u
ON u.user_id = CASE WHEN :user_logged = m.from THEN m.to ELSE m.from END as other_user_id
WHERE :user_logged IN (m.from, m.to)
Это ON
предложение также может быть записано как
ON (u.user_id = m.from AND :user_logged = m.to)
OR (u.user_id = m.to AND :user_logged = m.from)
или
ON u.user_id IN (m.from, m.to) AND u.user_id <> :user_logged;
кстати. Выберите то, что вам больше нравится.
И вот способ использовать параметр user ID только один раз в запросе:
SELECT m.msg, u.user_id, u.username, u.avatar
FROM (select :user_logged as user_id) myself
JOIN last_msg m ON myself.user_id IN (m.from, m.to)
JOIN users u ON u.user_id IN (m.from, m.to) AND u.user_id <> myself.user_id;
Комментарии:
1. Поправьте меня, если я ошибаюсь, согласно сценарию OP, объединение в порядке, верно? или необходимо включить объединение всех?
2. @Ajan Balakumaran: Да,
UNION
здесь тоже сработало бы. Но вопрос «нужно ли включатьUNION ALL
?» вводит в заблуждение.UNION ALL
это простая команда, которая просто склеивает два набора данных.UNION DISTINCT
(илиUNION
для краткости) — более сложная команда. Он склеивает два набора данных и удаляет дубликаты. Таким образом, вопрос скорее должен быть: «Нужно ли применятьUNION
здесь?» и ответ будет: «Нет, это не так. Достаточно применитьUNION ALL
«.3. Спасибо за быстрый ответ. Я вижу, что ОБЪЕДИНЕНИЕ ALL — это небольшие накладные расходы, поскольку оно не удаляет дубликаты. слава
4. @Ajan Balakumaran: Кажется, вы неправильно поняли.
UNION ALL
не имеет накладных расходов.UNION
делает. Представьте, что у меня есть две колоды игральных карт, одна из которых содержит только карты сердец, другая содержит только карты с номерами (т. Е. Без валетов, дам и т. Д.). Теперь вы просите меня либо отдать вам все карты, либо попросить меня отдать вам все карты, но предварительно удалить дубликаты карт, чтобычтобы получить каждую карту (например, семерку червей) только один раз. Какая из двух задач больше подходит для меня?5. @Ajan Balakumaran: Ах, хорошо, вы имели в виду «меньше» накладных расходов. Извините за недоразумение 🙂