Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP Puzzles. Авторы: Кузнецов М.В., Симдянов И.В. Объектно-ориентированное программирование на PHP. Авторы: Кузнецов М.В., Симдянов И.В. Социальная инженерия и социальные хакеры. Авторы: Кузнецов М.В., Симдянов И.В. PHP 5/6. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: сложная выборка переписки
 
 автор: Дмитрий Смаль   (09.12.2010 в 14:08)   письмо автору
 
 

Здравствуйте

есть таблица сообщений message
id - номер сообщения
sender - отправитель
resiver - получатель
date - дата
text - текст

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

соответственно он может быть как получатель так и отправитель

эту задачу я уже решил на 50%
добавил поле users в котором хранится sender+resiver (сумма ИД пользователей переписки)

такой запрос
SELECT * FROM `message` WHERE sender=1 OR resiver=1 GROUP BY users
возвращает мне по одному первому сообщению из всех переписок пользователя 1

но как сделать чтобы это были последние сообщения?

  Ответить  
 
 автор: Дмитрий Смаль   (09.12.2010 в 14:32)   письмо автору
 
   для: Дмитрий Смаль   (09.12.2010 в 14:08)
 

похоже что решил

SELECT
id,
resiver,
sender,
MAX (date) max_date
MAX(CONCAT(`date`, `text`)) text
FROM `message`
WHERE sender=1 OR resiver=1
GROUP BY users
ORDER BY max_date DESC

  Ответить  
 
 автор: Trianon   (09.12.2010 в 17:27)   письмо автору
 
   для: Дмитрий Смаль   (09.12.2010 в 14:32)
 

.

  Ответить  
 
 автор: Лена   (09.12.2010 в 21:58)   письмо автору
 
   для: Дмитрий Смаль   (09.12.2010 в 14:32)
 

Неправильно решили.

  Ответить  
 
 автор: Дмитрий Смаль   (10.12.2010 в 10:42)   письмо автору
 
   для: Лена   (09.12.2010 в 21:58)
 

а почему не написали как правильно? надо попросить?
если вы знаете более разумное решение, то буду очень вам признателен если сюда его напишете

да ошибку я уже увидел - resiver и sender возвращаются не те что в последней записи
получается надо что-ли все в конкат засунуть с разделителями а потом уже разбивать на пхп строку..

  Ответить  
 
 автор: Trianon   (10.12.2010 в 11:09)   письмо автору
 
   для: Дмитрий Смаль   (10.12.2010 в 10:42)
 

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

при попытке расширить количество получателей (которые, кстати орфографически корректно пишутся как receiver, а лексически как recipient ) сообщения до нескольких, вся Ваша идея с суммами ключей рушится, как карточный домик.

  Ответить  
 
 автор: Дмитрий Смаль   (10.12.2010 в 11:24)   письмо автору
 
   для: Trianon   (10.12.2010 в 11:09)
 

к сожалению исправление орфографических ошибок не помогло решить задачу, буду благодарен за предложение по сути вопроса

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

  Ответить  
 
 автор: Trianon   (10.12.2010 в 11:36)   письмо автору
 
   для: Дмитрий Смаль   (10.12.2010 в 11:24)
 

Я Вам ошибки логики агрегатного группирующего запроса исправить предложил.
Еще раз. Нельзя в списке SELECT упоминать поля, которые не перечислены в GROUP BY и не входят при этом в агрегатные функции (такие как MAX COUNT и им подобные).
(Да, из этого правила есть неформальное исключение, но Вы сперва научитесь решать задачу честно, а потом рассчитывайте на неформальные послабления языка)

Орфография - так.. всего лишь указание, что читать противно.


>напомню что суть вопроса как сделать вышеупомянутый скл запрос
ну это Ваша суть - моя совсем другая.

  Ответить  
 
 автор: Лена   (10.12.2010 в 12:46)   письмо автору
 
   для: Дмитрий Смаль   (10.12.2010 в 10:42)
 

>а почему не написали как правильно?

Потому что надо тратить время на составление запроса. А вы выше написали, что решение нашли и оно вас удовлетворило. Значит, вам оно не сильно и надо. Поэтому и написала одной фразой. Заинтересуетесь, напишу дальше, нет - мне свое время дорого.

>надо попросить?
Можно и попросить. Все, в том числе и я, приходят сюда просить. И от того, как попросят, такой будет и ответ. Так что сбавьте тон. Гонора у всех всегда хватает и его показывать здесь необязательно. Тем более в роли просящего.

>если вы знаете более разумное решение, то буду очень вам признателен если сюда его напишете
>да ошибку я уже увидел - resiver и sender возвращаются не те что в последней записи
>получается надо что-ли все в конкат засунуть с разделителями а потом уже разбивать на пхп строку..

Никакого дополнительного поля users, я бы не делала. В нем нет смысла.
Допустим, у нас пользователь с идентификатором 1.
Входящие сообщения, например, можно так:

SELECT mess1.id, mess1.date, mess1.text, mess1.receiver, mess1.sender
FROM message mess1
WHERE mess1.receiver =1
AND mess1.date
IN (

SELECT MAX( date )
FROM `message`
GROUP BY sender
)


Исходящие:

SELECT mess1.id, mess1.date, mess1.text, mess1.receiver, mess1.sender
FROM message mess1
WHERE mess1.sender =1
AND mess1.date
IN (

SELECT MAX( date )
FROM `message`
GROUP BY receiver
)


Можно попробовать соединить эти два запроса в один.
Явный вариант - через UNION.

  Ответить  
 
 автор: Дмитрий Смаль   (10.12.2010 в 16:06)   письмо автору
 
   для: Лена   (10.12.2010 в 12:46)
 

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

насчет объединения, то я раньше так и делал, но потом пришла мысль добавить поле users для оптимизации запроса, так как на мой взгляд с объединением будет работать медленнее

вот еще получилось одно интересное решение
SELECT m.*
FROM message m
LEFT JOIN message m2 ON (m2.users=m.users AND m2.date > m.date)
WHERE (m.sender=1 OR m.resiver=1) AND m2.id IS NULL
GROUP BY m.id
ORDER BY m.date DESC

в нем можно даже поле users не использовать

надо будет протестировать что работает быстрее, с вложенным запросом или с left join

  Ответить  
 
 автор: Лена   (10.12.2010 в 17:22)   письмо автору
 
   для: Дмитрий Смаль   (10.12.2010 в 16:06)
 

>спасибо за понятный человеческий ответ:)
>суть я понял, тоже над этим думал, но вложенный запрос неохота делать

если думали, почему в первом посте не написали?

>вот еще получилось одно интересное решение
>
SELECT m.*
>FROM message m
>LEFT JOIN message m2 ON (m2.users=m.users AND m2.date > m.date)
>WHERE (m.sender=1 OR m.resiver=1) AND m2.id IS NULL
>GROUP BY m.id
>ORDER BY m.date DESC

>в нем можно даже поле users не использовать

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

  Ответить  
 
 автор: Trianon   (10.12.2010 в 16:18)   письмо автору
 
   для: Лена   (10.12.2010 в 12:46)
 

>

SELECT mess1.id, mess1.date, mess1.text, mess1.receiver, mess1.sender
FROM message mess1
WHERE mess1.sender =1
AND mess1.date
IN (

SELECT MAX( date )
FROM `message`
GROUP BY receiver
)


лучше бы тут не применять date в качестве ключа отбора.
Если первичный ключ - автоинкрементный, и создает монотонную последовательность, лучше положиться на него, кмк.
Дата... она ведь может и совпасть...

  Ответить  
 
 автор: Лена   (10.12.2010 в 17:24)   письмо автору
 
   для: Trianon   (10.12.2010 в 16:18)
 

>лучше бы тут не применять date в качестве ключа отбора.
>Если первичный ключ - автоинкрементный, и создает монотонную последовательность, лучше положиться на него, кмк.
>Дата... она ведь может и совпасть...

Согласна. Только через первичный ключ сложнее выборка будет...

  Ответить  
 
 автор: Trianon   (10.12.2010 в 17:35)   письмо автору
 
   для: Лена   (10.12.2010 в 17:24)
 

не понял?
если я тупо заменю у Вас date на id ?

SELECT mess1.id, mess1.date, mess1.text, mess1.receiver, mess1.sender
  FROM message mess1
  WHERE mess1.sender =1
  AND mess1.id
  IN (

        SELECT MAX( id )
          FROM `message`
          GROUP BY receiver
    ) 

  Ответить  
 
 автор: Дмитрий Смаль   (11.12.2010 в 10:18)   письмо автору
 
   для: Trianon   (10.12.2010 в 17:35)
 

ну ведь можешь подсказать если захочешь :)

вот такой запрос получился
SELECT m.*
FROM message m
WHERE m.id = (SELECT MAX(id) FROM message WHERE (sender=1 OR receiver=1) AND users=m.users)
ORDER BY m.date DESC


еще два варианта получилось, но наверно с вложенным запросом будет работать быстрее всего
SELECT max(`date`) max_date, MAX(CONCAT(`date`,' ',`id`,' ',`receiver`,' ',`sender`,' ',`text`)) content
FROM `message`
WHERE sender=1 OR receiver=1
GROUP BY users
ORDER BY max_date DESC


SELECT m.*
FROM message m
LEFT JOIN message m2 ON ((m2.sender=1 OR m2.receiver=1) AND m2.users=m.users AND m2.date > m.date)
WHERE (m.sender=1 OR m.receiver=1) AND m2.id IS NULL
ORDER BY m.date DESC


в любом случае поле users нужно

еще появилась мысль вместо дополнительного поля users добавить поле last в котором просто обозначать последнее сообщение в теме
SELECT * FROM message WHERE (sender=1 OR receiver=1) AND last=1 ORDER  BY date DESC
такой запрос будет быстрее всех работать, но только перед каждым добавлением сообщения в переписку нужно делать
UPDATE message SET last=0 WHERE (sender=1 AND receiver=2) OR (sender=2 AND receiver=1)
чтобы затирать пометку последнего сообщения

всем спасибо за интерес к теме, думаю что вопрос полностью исчерпан

  Ответить  
 
 автор: Trianon   (11.12.2010 в 12:27)   письмо автору
 
   для: Дмитрий Смаль   (11.12.2010 в 10:18)
 

Вы явно меня с кем-то спутали.
Мой ответ был адресован Лене.

  Ответить  
 
 автор: Лена   (13.12.2010 в 11:04)   письмо автору
 
   для: Trianon   (10.12.2010 в 17:35)
 

Если тупо заменить, то вроде как смысл теряется... Или я чего-то недопоняла.
Выбирается ж по максимальной дате, если заменяем date на id, возможен же вариант, когда id будет максимальным, но дата при этом - не максимальная, а нам надо поледние сообщения.

  Ответить  
 
 автор: Trianon   (13.12.2010 в 13:27)   письмо автору
 
   для: Лена   (13.12.2010 в 11:04)
 

в принципе, если сообщению позже через UPDATE могут корректировать дату , то да, такое может случиться.
С другой стороны, дата не обязана быть уникальным ключом - тогда как Ваш запрос исправлять?

  Ответить  
 
 автор: Лена   (14.12.2010 в 00:05)   письмо автору
15.4 Кб
 
   для: Trianon   (13.12.2010 в 13:27)
 

Смотрите, в аттаче - ситуация, когда дата совпала.
Получается один и тот же пользователь за одну секунду запостил два сообщения. И эти два сообщения моим запросом выведены как максимальные.
Чтобы уменьшить вероятность совпадения, можно сделать тип поля даты int, и забивать это поле временем с микросекундами - microtime(), и тогда забить два сообщения за одну микросекунду это надо будет еще постараться.

  Ответить  
 
 автор: Trianon   (14.12.2010 в 00:26)   письмо автору
 
   для: Лена   (14.12.2010 в 00:05)
 

дело же не в вероятности?
если вопрос в том чтобы получить последнюю созданную запись в таблице - этому акценту автоинкрементный первичный ключ (вместо поля date) удовлетворяет целиком и полностью.
Тогда хоть усоздавайся этих сообщений - все равно совпадений не будет.

  Ответить  
 
 автор: Лена   (14.12.2010 в 11:29)   письмо автору
 
   для: Trianon   (14.12.2010 в 00:26)
 

>если вопрос в том чтобы получить последнюю созданную запись в таблице

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

PS. Вообще-то здесь с самого первого поста идея как бы неправильная. Надо не по максимальной дате вытягивать, а по последнему посещению пользователя. Фиксируем последний заход пользователя на сайт и при следующем его заходе показываем ему только те сообщения других пользователей, которых он не читал. Зачем ему видеть последнее сообщение(с максимальной датой), если он его уже читал в прошлый раз.
Тогда и будет ориентация на первичный ключ.

  Ответить  
 
 автор: Trianon   (14.12.2010 в 12:18)   письмо автору
 
   для: Лена   (14.12.2010 в 11:29)
 

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

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



>Поэтому и придумываются всякие варианты, чтобы на поле, которое не уникально, вероятность совпадений была мала.

>PS. Вообще-то здесь с самого первого поста идея как бы неправильная.

Кто б спорил. Но если все время думать за того, кто спрашивает, сам он не начнет думать никогда.

  Ответить  
 
 автор: Лена   (15.12.2010 в 00:10)   письмо автору
 
   для: Trianon   (14.12.2010 в 12:18)
 

Сдаюсь :)
Оставим ваш вариант за 10.12.2010 в 17:35.

  Ответить  
 
 автор: Trianon   (15.12.2010 в 01:29)   письмо автору
 
   для: Лена   (15.12.2010 в 00:10)
 

лучше бы за 09.12.2010 в 17:27 :)
За него хоть не так совестно. :)

  Ответить  
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования