SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 24.04.2012 20:48:38

Сергей133
Участник
Зарегистрирован: 24.04.2012
Сообщений: 4

Сложный запрос для переписки

Всем привет. Столкнулся с проблемкой. Имеется таблица в БД, где хранятся сообщения всех пользователей.
Имеется 3 основных поля:
ID_message - id сообщения
ID_user - id автора сообщения
ID_user_to - id адресата

При переходе в личные сообщения пользователь должен увидеть список всех своих сообщений, уникальных по пользователю. Выбрав которое он может просмотреть всю переписку с выбранным пользователем. Нужен запрос для получения информации для отображения этих сообщений. С SQL я не дружу и сделал такую "красоту" :

$currentUserId = User_User::getId(); - id пользователя, которым смотрит свою переписку

Получаем id всех пользователей с которыми вел переписку наш пользователь сообщения( где он является автором либо адресатом)
        $stmt = "SELECT `message`.`ID_user_to` id
                  FROM `message`
                 WHERE `message`.`ID_user`='" . $currentUserId . "'
                 UNION
                 SELECT `message`.`ID_user` id
                 FROM `message`
                 WHERE `message`.`ID_user_to`='" . $currentUserId . "'";
        $arrIds =  $objDB->selectSimpleArray($stmt); // Вся переписка текущего пользователя
       
Перебираем эти id, для каждого пользователя смотрим все сообщения, где он автор а получатель наш пользователь или он - адресат, а автор - наш пользователь, сортируем по дате и берём последнее...Но мой вариант вообще не работает, я знаю почему, но просто не знаю как реализовать правильно + с наилучшим быстродействием..... 
foreach ($arrIds as $id) {
            $stmt = "SELECT `message`.`text`,`message`.`created_at`,`message`.`is_read_at`,       `user`.`e_mail`
                                    FROM `message`
                                    IF(`message`.`ID_user`='". $currentUserId  ."') INNER JOIN `user`
                                    ON `user`.`ID_user` = `message`.`ID_user_to`)
                                    IF(`message`.`ID_user_to`='" . $currentUserId . "')INNER JOIN `user`
                                    ON `user`.`ID_user`=`message`.ID_user)
                                    WHERE `message`.`ID_user`='" . $id . "' AND `message`.`ID_user_to`='" . $currentUserId . "' OR `message`.`ID_user_to`='" . $id . "' AND `message`.`ID_user`='" . $currentUserId . "' ORDER BY `message`.`created_at` DESC LIMIT 1";
            $arrMessages[] = $objDB->selectOne($stmt); // Получаем последнее сообщение переписки пользователя с собеседником $id
        }
        return $arrMessages;

Подскажите, пожалуйста, возможные решения этой задачи....

Отредактированно Сергей133 (24.04.2012 21:52:41)

Неактивен

 

#2 24.04.2012 23:54:16

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Сложный запрос для переписки

Неактивен

 

#3 25.04.2012 13:18:05

Сергей133
Участник
Зарегистрирован: 24.04.2012
Сообщений: 4

Re: Сложный запрос для переписки

Легче не стало, попробовал все  описанные там способы, везде одна и та же проблема...
Требуется получить для текущего пользователя всю последнюю переписку. Допусти Id пользователя, который зашёл в "Мои сообщения" = 5. В Базе данных имеется 5 записей с таким id:
ID_user = 5 ID_user_to = 2 created_at = 01.01.2000
ID_user = 5 ID_user_to = 2 created_at = 02.01.2000
ID_user = 3 ID_user_to = 5 created_at = 01.01.2000
ID_user = 2 ID_user_to = 5 created_at = 03.01.2000
ID_user = 5 ID_user_to = 3 created_at = 07.01.2000
Значит пользователь должен увидеть две строки:
Переписка с пользователем ID=3 Последнее сообщение: 07.01.2000, текст сообщения
Переписка с пользователем ID=2 Последнее сообщение: 03.01.2000, текст сообщения

Я сделал запрос:

SELECT ID_user,ID_user_to, MAX(created_at)
FROM `message`
WHERE ID_user = 5 OR ID_user_to = 5
GROUP BY ID_user,ID_user_to
ORDER BY created_at DESC
В этом примере он вернёт строки :

ID_user = 5 ID_user_to = 3 created_at = 07.01.2000
ID_user = 2 ID_user_to = 5 created_at = 03.01.2000
ID_user = 5 ID_user_to = 2 created_at = 02.01.2000
ID_user = 3 ID_user_to = 5 created_at = 01.01.2000
Как сделать так, чтобы вместо этого он выводил:
ID_user = 5 ID_user_to = 3 created_at = 07.01.2000
ID_user = 2 ID_user_to = 5 created_at = 03.01.2000 ?

Ведь сообщения от id=5 для id=2 и от id=2 для id=5 относятся к одной и той же переписке.

Неактивен

 

#4 25.04.2012 18:16:38

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Сложный запрос для переписки

SELECT ID_user,ID_user_to, MAX(created_at) from
(SELECT ID_user,ID_user_to, created_at FROM `message` WHERE ID_user = 5
union
SELECT ID_user,ID_user_to, created_at FROM `message` WHERE ID_user_to = 5) t group by ID_user order by 3 desc;
 

Неактивен

 

#5 25.04.2012 22:24:31

evgeny
Гуру
Зарегистрирован: 04.05.2009
Сообщений: 335

Re: Сложный запрос для переписки

vasya написал:

SELECT ID_user,ID_user_to, MAX(created_at) from
(SELECT ID_user,ID_user_to, created_at FROM `message` WHERE ID_user = 5
union
SELECT ID_user,ID_user_to, created_at FROM `message` WHERE ID_user_to = 5) t group by ID_user order by 3 desc;
 

По моему group by ID_user тут испортит малину в случае если есть только такие записи.
ID_user = 5 ID_user_to = 2 created_at = 01.01.2000
ID_user = 5 ID_user_to = 3 created_at = 07.01.2000


Первое что пришло в голову

SELECT MAX(created_at),ID_user,ID_user_to,cross_id FROM (
SELECT ID_user,ID_user_to, created_at,(ID_user+ID_user_to) cross_id FROM `message` WHERE ID_user = 5 OR ID_user_to = 5
) GROUP BY cross_id order by 1 desc;

Неактивен

 

#6 27.04.2012 11:19:16

Сергей133
Участник
Зарегистрирован: 24.04.2012
Сообщений: 4

Re: Сложный запрос для переписки

Последний вариант тоже неверен. Группируя, он берёт абсолютно случайные сообщения, добавляя при этом в конце ячейку с датой последнего сообщения переписки.

Отредактированно Сергей133 (27.04.2012 11:20:12)

Неактивен

 

#7 27.04.2012 11:35:23

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Сложный запрос для переписки

evgeny написал:

По моему group by ID_user тут испортит малину в случае если есть только такие записи.
ID_user = 5 ID_user_to = 2 created_at = 01.01.2000
ID_user = 5 ID_user_to = 3 created_at = 07.01.2000

Да, вы правы, что-то совсем не то я написал. Правильно будет

SELECT ID_user,ID_user_to, MAX(created_at) from
(SELECT ID_user, ID_user_to, created_at FROM `message` WHERE ID_user = 5
union
SELECT ID_user_to, ID_user created_at FROM `message` WHERE ID_user_to = 5) t group by 1,2 order by 3 desc;



evgeny написал:

Первое что пришло в голову

SELECT MAX(created_at),ID_user,ID_user_to,cross_id FROM (
SELECT ID_user,ID_user_to, created_at,(ID_user+ID_user_to) cross_id FROM `message` WHERE ID_user = 5 OR ID_user_to = 5
) GROUP BY cross_id order by 1 desc;

В этом варианте нужно будет делать join, так как ID_user,ID_user_to выбираются без группирующей функции.

Неактивен

 

#8 27.04.2012 16:04:31

Сергей133
Участник
Зарегистрирован: 24.04.2012
Сообщений: 4

Re: Сложный запрос для переписки

Последний запрос тоже неверен, там при группировке могут меняться местами id_user и id_user_to  у сообщений....

Нашёл решение:
SELECT `user`.`e_mail`,`user`.`ID_user`, `temptable`.*
                 FROM `user`
                 LEFT JOIN (
                            SELECT `message`.*
                            FROM `message`
                            WHERE `message`.`ID_user`=5 OR `message`.`ID_user_to`=5
                            ORDER BY `message`.`created_at` DESC
                           )temptable
                ON (`user`.ID_user = `temptable`.`ID_user`) OR (`user`.ID_user = `temptable`.`ID_user_to`)
                WHERE `user`.`ID_user` IN (
                                            SELECT `ID_user`
                                            FROM `message`
                                            WHERE `ID_user_to` = 5
                                            UNION
                                            SELECT `ID_user_to`
                                            FROM `message`
                                            WHERE `ID_user` = 5
                                          )
                GROUP BY `user`.`ID_user`

Отредактированно Сергей133 (27.04.2012 16:54:29)

Неактивен

 

#9 27.04.2012 22:28:03

evgeny
Гуру
Зарегистрирован: 04.05.2009
Сообщений: 335

Re: Сложный запрос для переписки

Сергей133 написал:

Последний запрос тоже неверен, там при группировке могут меняться местами id_user и id_user_to  у сообщений....

Нашёл решение:
SELECT `user`.`e_mail`,`user`.`ID_user`, `temptable`.*
                 FROM `user`
                 LEFT JOIN (
                            SELECT `message`.*
                            FROM `message`
                            WHERE `message`.`ID_user`=5 OR `message`.`ID_user_to`=5
                            ORDER BY `message`.`created_at` DESC
                           )temptable
                ON (`user`.ID_user = `temptable`.`ID_user`) OR (`user`.ID_user = `temptable`.`ID_user_to`)
                WHERE `user`.`ID_user` IN (
                                            SELECT `ID_user`
                                            FROM `message`
                                            WHERE `ID_user_to` = 5
                                            UNION
                                            SELECT `ID_user_to`
                                            FROM `message`
                                            WHERE `ID_user` = 5
                                          )
                GROUP BY `user`.`ID_user`

Это очень тяжёлый запрос.

Вот, проверьте этот вариант , по идее должен быть самым оптимальным вариантом.

SELECT if(ID_user=5,ID_user_to,ID_user) my_friend, max(created_at) FROM `message`
WHERE ID_user = 5 OR ID_user_to = 5 group by if(ID_user=5,ID_user_to,ID_user)
order by 2 desc;

Отредактированно evgeny (27.04.2012 22:29:21)

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson