2 левых соединения объединяют, умножая результаты. Но это не должно

#sql #sql-server #join #left-join

#sql #sql-server #Присоединиться #левое соединение

Вопрос:

Я создаю сайт чата и для представления чатов аудитории у меня есть 3 выпадающих списка — Спорт (по умолчанию все виды спорта), День / месяц / год, Пользователи онлайн / Общее количество пользователей.

Теперь, если по умолчанию выбрано all sports, и я выбираю 1 месяц и общее количество пользователей, ожидаемый результат должен быть

Мой запрос

 SELECT DISTINCT roo.[Sports], 
                roo.[Name], 
                COUNT(DISTINCT chu.ChatUserLogId) AS TotalUsers, 
                COUNT(DISTINCT liu.[LoggedInUserID]) AS UserOnline

 FROM Room AS roo 

 LEFT JOIN LoggedInUser AS liu ON roo.RoomID = liu.RoomID 
 LEFT JOIN ChatUserLog  AS chu ON roo.RoomID = chu.RoomID 
               AND chu.LoggedInTime >= DATEADD(DAY,-30,GETDATE()) 

 GROUP BY roo.[Sports], roo.[Name]   
 ORDER BY TotalUsers DESC
  

Один человек предположил, что с помощью моего метода я фактически умножаю строку из-за двух объединений, поэтому мне нужно сначала объединить, а затем объединить.

Итак, в конце концов, я тоже попробовал этот запрос

 with agg_ChatUserLog as (select RoomId, count(*) as cnt_user_tot from ChatUserLog WHERE LoggedInTime >= DATEADD(DAY,-30,GETDATE()) group by RoomId),                                            

       agg_LoggedInUser as (select RoomId, count(*) as cnt_user_logged from LoggedInUser group by RoomId)

select Sports, Name, cnt_user_tot, cnt_user_logged from Room r 

       left outer join agg_ChatUserLog acu on acu.RoomId = r.RoomId 

       left outer join agg_LoggedInUser alu on alu.RoomId = r.RoomId;
  

Но это также умножает результаты.

Где я допускаю ошибку в обоих запросах? Заранее спасибо и хорошего дня впереди.

ОБРАЗЕЦ ДАННЫХ:

 Chatroom name   Total Users   Online users               

Basketball
Roomname27          32              5
Roomname11          15              3
Roomname32           8              1 

Football
Roomname5           63              12
Roomname18          44               7
Roomname4           15               2
  

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

1. Второй способ выглядит неплохо. Добавьте примерные данные и результат вашего второго запроса, который мы проверим

2. В вопросе, а не в комментарии

3. @Prdp Я добавил образцы в вопрос для вашей справки. Спасибо и с нетерпением жду вашей помощи.

4. Это пример вывода.

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

Ответ №1:

вы можете попробовать другой синтаксис :

 select Sports, 
    Name, 
    coalesce(
        ( 
            select count(distinct C.ChatUserLogId) 
            from ChatUserLog as C
            where C.RoomId = R.RoomId 
              and C.LoggedInTime >= DATEADD(DAY,-30,GETDATE()) 
        ), 
    0) AS TotalUsers,
    coalesce(
        (
            select count(distinct D.LoggedInUserID)
            from LoggedInUser as D
            where D.RoomId = R.RoomId 
        ),
    0) AS UserOnline
from Room R 
  

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

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

1. Сначала мы кодировали именно так… Но это делает дело еще хуже. Сначала агрегирование, а затем выбор данных приводит к повторению только спортивных чатов, когда есть какие-либо ОНЛАЙН-ПОЛЬЗОВАТЕЛИ. Но в вашем запросе (а также в том, что мы попробовали первым), хотя онлайн-пользователей нет, он просто повторяет все спортивные чаты. Умножаются данные о пользователях ОНЛАЙН и ОБЩЕЙ таблице ПОЛЬЗОВАТЕЛЕЙ. Не могли бы вы догадаться, почему возникает эта ошибка? Спасибо

2. Я предполагаю, что проблема с элементами «GROUP BY» тоже … «с agg_ChatUserLog как (выберите roomId, посчитайте( ) как cnt_user_tot из ChatUserLog, ГДЕ LoggedInTime >= DATEADD(DAY,-30,GETDATE()) группируйте по roomId), agg_LoggedInUser как (выберите roomId, посчитайте ( ) как cnt_user_logged из Войдите в группу пользователей по идентификатору комнаты) выберите Спорт, Имя, cnt_user_tot, cnt_user_logged из комнаты r слева от входа, присоединитесь к agg_ChatUserLog acu на acu. roomId = r.roomId левое внешнее соединение agg_LoggedInUser alu в alu. roomId = r.roomId ГРУППИРОВАТЬ ПО видам спорта, cnt_user_tot УПОРЯДОЧИВАТЬ По cnt_user_tot ОПИСАНИЮ»