Выберите разговор между точными пользователями (по идентификаторам пользователей) - PullRequest
0 голосов
/ 26 мая 2019

Я просыпаюсь в простом приложении чата.Это должно позволить пользователям вести беседы от пользователя к пользователю или в группах (несколько пользователей).Итак, вот мои таблицы:

table users
ID   | username      | ...

table conversations
ID   | ...

table conversations_users
ID   | user_id       | conversation_id

Допустим, я выбрал несколько пользователей с идентификаторами 11, 22 и 33 и хочу проверить, является ли разговор между этими точными пользователями (два,три или более) уже существует в базе данных.Я мог бы достичь этого с помощью нескольких запросов и некоторых внутренних операций, но я уверен, что это сильно повлияет на производительность.

Возможно ли даже с одним запросом?


Дополнительный вопрос: если есть простое решение, будет ли оно эффективно для действительно длинных таблиц (например, 1.000.000 разговоров,~ 3.000.000 строк вversations_users) и многопользовательские запросы ( давайте проверим, существует ли диалог между 100 пользователями )?

Ответы [ 2 ]

1 голос
/ 26 мая 2019

Когда вы говорите:

... был ли разговор между этими точными пользователями ...

Я понимаю, что вы хотите в этом разговоре только этих пользователей и никого больше.
В этом случае просто:

sum(case when user_id in (11, 22, 33) then 1 else 0 end) = 3

не дает правильного результата, потому что он вернул бы все conversation_id s, где участвуют эти 3 пользователя, но, возможно, с другими.
Вам нужно сравнить с count(*):

select conversation_id
from conversation_users
group by conversation_id
having sum(user_id in (11, 22, 33)) = count(*);

Я считаю, что для каждого conversation_id нет дубликатов user_id, поэтому count(distinct user_id) не требуется.
В случае разговора между этими 3 пользователями и, возможно, другими, вы можете использовать предложение where:

select conversation_id
from conversation_users
where user_id in (11, 22, 33)
group by conversation_id
having count(*) = 3;
0 голосов
/ 26 мая 2019

Один метод агрегации:

select cu.conversation_id
from conversation_users cu
group by cu.conversation_id
having sum(case when cu.user_id in (11, 22, 33) then 1 else 0 end) = 3;

С точки зрения производительности это может быть быстрее:

select c.*
from conversations c
where exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id = 11
                 ) and
     exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id = 22
                 ) and
     exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id = 33
                 ) and
           not exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id not in (11, 22, 33)
                 ) ;

Для этого можно воспользоваться индексом conversation_users(user_id).

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

...