Запрос для поиска отдельной строки для объединения

#sql #sql-server #aggregate-functions #distinct #string-aggregation

#sql #sql-сервер #агрегатные функции #distinct #строка-агрегация

Вопрос:

У меня есть таблица базы данных с именем logoninfo . Ниже приведена схема.

    Record ID|User ID | Computer ID |LOGON_TIME|LOGOFF_TIME
 

Если время выхода из системы равно 0, то пользователь в данный момент зарегистрирован на компьютере.
Имя пользователя находится в отдельной таблице с именем «user» со схемой

    user id  | user name 
 

Имя компьютера находится в отдельной таблице с именем «компьютер» со схемой

     computer id | computer name 
 

Мне нужно иметь отчет, в котором есть имя пользователя и соответствующий ему текущий зарегистрированный компьютер (разделенный запятой)

         user name | computer names
 

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

 select user.name as USER_NAME,
    userLogon."comps" as COMPUTERS
from user
    inner join (
        select user_id as "user_resource",
            string_agg(compRes.name, ' ,') as "comps"
        from logoninfo
            inner join computer compRes on logoninfo.computer_id = compRes.computer_id
        where logoninfo.logoff_time = 0
        group by logoninfo.user_id
    ) userLogon on userLogon."user_resource" = user.user_id
 

Итак, я попытался добавить distinct в string_agg функцию, но это невозможно в SQL Server.

Пожалуйста, помогите мне с запросом к требуемому отчету.

Заранее спасибо.

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

1. Примерные данные и ожидаемые результаты сделают это более понятным. DDL DML сделали бы это потрясающе.

Ответ №1:

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

 SELECT u.name as [USER_NAME]
     , l.comps as [COMPUTERS]
FROM #user u
OUTER APPLY (
  SELECT STRING_AGG(c.name, ', ') as [comps]
  FROM (
    SELECT c.name
    FROM #logoninfo l
    JOIN #computer c ON l.computer_id = c.computer_id
    WHERE l.user_id = u.user_id
      AND l.logoff_time = 0
    GROUP BY c.name
  ) c
) l
 

И результат:

введите описание изображения здесь

Ответ №2:

Вот способ сделать это.

Ранняя фильтрация и агрегирование с использованием string_agg

 select userLogon.user_resource
         , string_agg(compRes.name, ' ,') as concat_computer
    from (
          select distinct 
                  user_id as "user_resource",
                  compRes.name as "comps"
            from logoninfo
            join computer compRes 
              on logoninfo.computer_id = compRes.computer_id
            join user us
              on logonginfo.user_id=us.user_id
            where logoninfo.logoff_time = 0
          ) userLogon
  group by userLogon.user_resource

      
 

Ответ №3:

Я бы использовал коррелированный подзапрос или боковое соединение:

 select u.*, c.*
from users u
cross apply (
    select string_agg(name) as all_computers
    from (
        select distinct c.name
        from computer c
        inner join logoninfo li on li.computer_id = c.computer_id
        where li.user_id = u.user_id and li.logoff_time = 0
    ) c
) c