За последние 24 часа нас посетили 22614 программистов и 1280 роботов. Сейчас ищут 815 программистов ...

Как вам такой MySQL запрос?

Тема в разделе "MySQL", создана пользователем artmirartem, 8 фев 2019.

  1. artmirartem

    artmirartem Новичок

    С нами с:
    25 сен 2018
    Сообщения:
    7
    Симпатии:
    0
    Как вам такой MySQL запрос? Не будет ли она сильно нагружать БД? Как его можно оптимизировать?

    Код (Text):
    1. SELECT D.id,sender_id,recipient_id,send_notifications, S.id AS sender_id, R.id AS recipient_id, R.name AS recipient_name, R.birthday AS recipient_birthday, R.status AS recipient_status, R.sex AS recipient_sex, R.last_activity_at AS recipient_last_activity_at, BS.id AS im_blakcklist, BR.id AS youare_blakcklist, LM.id AS last_message
    2. FROM dialogs D
    3.     JOIN users S on S.id=IF(93=sender_id, sender_id, recipient_id)
    4.     JOIN users R on R.id=IF(93=recipient_id, sender_id, recipient_id)
    5.     LEFT JOIN blacklist BS on BS.uid=S.id and BS.who=R.id
    6.     LEFT JOIN blacklist BR on BR.uid=R.id and BR.who=S.id
    7. WHERE ((sender_id=93 and recipient_id!=93) or (sender_id!=93 and recipient_id=93))
    8. ORDER BY id DESC LIMIT 0,16
    Там где 93, будет передаваться ID текущего пользователя
     
  2. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.076
    Симпатии:
    1.237
    Адрес:
    там-сям
    да почти никак. условие OR не дружит с индексами.

    мне кажется, здесь два раза лишнее условие с != ибо незачем скрывать от пользователя переписку с самим собой ))) она вообще есть?
    но я не думаю, что упрощение до (sender_id=93 or recipient_id=93) радикально улучшит скорость. можешь попытаться заменить его на UNION или UNION ALL

    Код (Text):
    1. SELECT blablabla... JOIN tyctyctyc
    2. WHERE sender_id=:ID
    3.   UNION ALL
    4. SELECT blablabla... JOIN tyctyctyc
    5. WHERE recipient_id=:ID
    это выглядит хуже, чем работает ;) попробуй и сравни
    --- Добавлено ---
    P.S. предполагаю, что у тебя есть индексы по sender_id и recipient_id
     
    artmirartem нравится это.
  3. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Я, чтоб не делать такие запросы, храню сообщения отдельно. У меня таблицы dialogs, messages, dialog_user. dialog_user - это, соответственно, связующая таблица для "многие-ко-многим", а в messages есть только from_id, recipient_id не нужен. В результате я делаю простую выборку по messages, и всё. Когда-то у меня получился подобный запрос, когда я начал как @artmirartem решать эту задачу, я ему ужаснулся, и решил переделать архитектуру. Пришёл к этому решению, и очень доволен. Уже на нескольких проектах проверено.
     
    artoodetoo нравится это.
  4. artmirartem

    artmirartem Новичок

    С нами с:
    25 сен 2018
    Сообщения:
    7
    Симпатии:
    0
    Как в таком случае проверить существует ли диалог между двумя пользователями?
     
  5. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Код (Text):
    1.  
    2. select du.dialog_id from dialog_user du join dialog_user du2 on du2.dialog_id=du.dialog_id  where du.user_id=1 and du2.user_id=4;
    Как-то так. Свой код прямо сейчас лень поднимать.
    --- Добавлено ---
    Самое крутое в моём решении, что оно подходит и для чатов с большим количеством участников.
     
  6. artmirartem

    artmirartem Новичок

    С нами с:
    25 сен 2018
    Сообщения:
    7
    Симпатии:
    0
    Список диалогов в таком случае для каждого пользователя можно получить так:
    Код (Text):
    1. SELECT dialog_id, DU.uid AS sender_id FROM fdy82_dialogs_users DU
    2.                        LEFT JOIN fdy82_dialogs D ON D.id=DU.dialog_id
    3.                        WHERE DU.uid=93 ORDER BY D.id DESC LIMIT 0,16
    Но как тогда получить имя пользователя и аватар, КОТОРОМУ адресовано сообщение. В списке диалогов у каждого пользователя эту информацию тоже мне нужно выводить..
    --- Добавлено ---
    в моем запросе (самом первом) я делал это с помощью JOIN'ов, и у меня заранее был известен получатель и отправитель
     
  7. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Не понял вопроса. У тебя диалог между двумя пользователями, которые заранее известны, так? Если один из них отправитель, то другой - получатель. На этом и основано моё решение не хранить получателя в messages, а хранить только id диалгога. А если речь идёт о последнем сообщении, то можно ввести поле last_message_id в таблицу dialogs, и подновлять его. Но твой проект, тебе решать. Я тебе это привёл не для того, чтобы сказать, что моё решение самое лучшее. Для меня это так, поскольку я получаю сообщения совсем элементарным select-ом. И остальное в принципе тоже не самыми сложными запросами. Иконку получателя правда мне ни разу не приходилось выводить, обычно идёт иконка отправителя.
     
  8. artmirartem

    artmirartem Новичок

    С нами с:
    25 сен 2018
    Сообщения:
    7
    Симпатии:
    0
    нет, заранее неизвестно, но мне нужно вытащить из БД все диалоги у текущего пользователя и вмест с диалогами вытащить имя и аватраку другого пользоателя в этом же лдиалоге
     
  9. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Ну я могу код Laravel показать. У меня все эти проекты на Laravel
    PHP:
    1. $models = Dialog::query()
    2.       ->withParticipant($user)
    3.       ->with("users")
    4.       ->with("lastMessage")
    5.       ->orderByDesc("last_message_date")
    6.       ->get();
    PHP:
    1. class Dialog extends Model
    2. {
    3.     use ReadCountable;
    4.     protected $casts = [
    5.         'deleted' => 'array',
    6.     ];
    7.  
    8.     public function messages()
    9.     {
    10.         return $this->hasMany(Message::class);
    11.     }
    12.  
    13.     public function users()
    14.     {
    15.         return $this->belongsToMany(User::class);
    16.     }
    17.  
    18.    
    19.    public function lastMessage()
    20.    {
    21.        return $this->hasOne(Message::class, "id", "last_message_id");
    22.    }
    23.  
    24.    
    25.    public function scopeWithParticipant(Builder $builder, $participant)
    26.    {
    27.        return $builder->join("dialog_user as du", "dialogs.id", "=", "du.dialog_id")
    28.            ->where("du.user_id", $participant instanceof User ? $participant->id: $participant)
    29.            ->where("du.hidden", 0);
    30.   }
    31. }
    Т.е. соответственно, некоторые вещи делаются в два запроса. Ну один или два запроса - для меня непринципиально. Много раз убеждался, что один или два на время выполнения заметным образом не влияет, влияет разница в десятки раз.
    --- Добавлено ---
    Можно и в один запрос нагородить, в принципе, если самому запросы все писать, не использовать Eloquent. Но я использую.
     
  10. artmirartem

    artmirartem Новичок

    С нами с:
    25 сен 2018
    Сообщения:
    7
    Симпатии:
    0
    Вот такой у меня запросик получился
    Код (Text):
    1. SELECT D.id,D.send_notifications, S.id AS sender_id, R.id AS recipient_id, R.name AS recipient_name, R.birthday AS recipient_birthday, R.status AS recipient_status, R.sex AS recipient_sex, R.last_activity_at AS recipient_last_activity_at, BS.id AS im_blakcklist, BR.id AS youare_blakcklist, PS.path as recipient_avatar FROM dialogs_users DU_S
    2.                        LEFT JOIN dialogs_users DU_R ON DU_R.dialog_id=DU_S.dialog_id
    3.                        LEFT JOIN dialogs D ON D.id=DU_S.dialog_id
    4.  
    5.                        JOIN users S ON S.id=DU_S.uid
    6.                        JOIN users R ON R.id=DU_R.uid
    7.  
    8.                        LEFT JOIN photos PS on PS.uid=S.id and PS.avatar=1
    9.                        LEFT JOIN photos PR on PR.uid=R.id and PR.avatar=1
    10.  
    11.                        LEFT JOIN blacklist BS on BS.uid=S.id and BS.who=R.id
    12.                        LEFT JOIN blacklist BR on BR.uid=R.id and BR.who=S.id
    13.  
    14.                        WHERE DU_S.uid=93 and DU_R.uid!=93 ORDER BY D.id DESC LIMIT 0,16
     
  11. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.076
    Симпатии:
    1.237
    Адрес:
    там-сям
    Оставлю здесь ссылочку на свой старый ответ по близкой теме:
    https://php.ru/forum/threads/sistema-soobschenij.57436/#post-462055
    в тот момент писалось на лету, не проверяя запросы, так что безошибочность не гарантирую )))