SQL select с динамическим подсчетом условий «МЕЖДУ» на основе объединенной таблицы

#mysql #sql #join #hql #between

#mysql #sql #Присоединиться #hql #между

Вопрос:

Я хочу добавить мессенджер в свой любимый проект, но у меня возникают трудности с написанием запросов к базе данных. Я использую MySQL для этой службы с гибернацией в качестве ORM. Почти все запросы были написаны на HQL, но в принципе я могу использовать собственные запросы.

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

Ниже я описал упрощенную структуру двух таблиц, важных для этой задачи.

Таблица сообщений:

ID текст временная метка
1 first_msg 1609459200
2 second_msg второй 1609545600

Таблица Member_event:

ID идентификатор пользователя Тип временная метка
1 1 1 1609459100
2 1 3 1609459300
3 1 2 1609459400
4 1 1 1609545500
 where type:
1 - user entered the chat,
2 - user leaved the chat,
3 - user cleared his own history of messages in the chat
 

Возможно ли прочитать все сообщения чата, доступные пользователю, одним запросом?

Я понятия не имею, как динамически проверять условия: ГДЕ временные метки сообщения находятся между всеми циклами «введено-оставлено» и после последнего «введено», если не оставлено, НО только после последней очистки истории. Если существует.

Комментарии:

1. Какая у вас версия MySQL?

2. версия MySQL @trincot — 8.0.20

3. Когда именно вводится -считывается оставленный? Если последнее событие равно 2? Как если бы 4-я строка не существовала в member_event? Тогда история ясности не имеет значения?

4. Чем то, что вы хотите, отличается от чтения всех сообщений в amp; позже последнего события, если только тип события не равен 2?

Ответ №1:

Я думаю, вы могли бы выполнить следующие шаги:

  1. возьмите объединение обеих таблиц и рассмотрите записи в порядке временной метки
  2. Используйте оконные функции, чтобы определить, был ли самый последний тип 1 или 2 равен 1. Мы можем использовать текущую сумму, где тип 1 добавляет единицу, а тип 2 вычитает единицу (а 3 ничего с этим не делает). С помощью другой оконной функции вы можете определить, существует ли еще следующий тип 3. Комбинация этих двух сведений может быть преобразована в 1, когда строка принадлежит интервалу, который должен быть собран, и в 0, когда нет.
  3. Отфильтруйте предыдущий результат, чтобы получить только записи сообщений, и только те, где вычисление составило 1.

Вот запрос:

 with unified as (
    select   id, text, timestamp, null as type
    from     message
    union
    select   id, null, timestamp, type 
    from     member_event
    where    user_id = 1),

validated as (
    select   unified.*,
             sum(case type when 1 then 1 when 2 then -1 else 0 end) 
                over (order by timestamp
                      rows unbounded preceding) * 
             min(case type when 3 then 0 else 1 end) 
                over (order by timestamp
                      rows between current row and unbounded following) valid
    from     unified
    order by timestamp)

select   id, text, timestamp
from     validated
where    type is null and valid = 1
order by timestamp
 

Комментарии:

1. Это ответило на ваш вопрос? Не могли бы вы дать обратную связь?

2. Привет, @trincot! Я опоздал, но да, благодаря вашему ответу и этим оконным функциям я переработал службу чата своего приложения.

Ответ №2:

Я не понимаю, как вы могли бы сопоставить таблицу Member_event с таблицей Message_table без дополнительного FOREIGN_KEY . Вы пытаетесь назначить сообщения, доступные пользователю, с помощью метки времени? Если это так, попробуйте следующее:

 SELECT * FROM MESSAGE_TABLE m
WHERE m.TIMESTAMP BETWEEN 
(SELECT TOP 1 TIMESTAMP FROM MEMBER_EVENT_TABLE WHERE type = 1 ORDER BY TIMESTAMP DESC) 
AND (SELECT TOP 1 TIMESTAMP FROM MEMBER_EVENT_TABLE WHERE type != 1 ORDER BY TIMESTAMP DESC)
 

Это, по крайней мере, должно показывать последние сообщения между join и clean / leave

Комментарии:

1. Есть много других полей, таких как chat_id и т.д. Я упростил таблицы для вопроса.

2. Я допустил незначительную ошибку при выборе. Просто исправил это: D

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

4. Сложно проверить ранее назначенные сообщения через SQL. Может работать с циклом WHILE, но я не знаю, как правильно это сделать 🙂

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