SQL — Объединение нескольких таблиц и выбор первой записи по условиям

#sql #sql-server #join

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

Вопрос:

У меня есть следующие таблицы:

 Projects (ID, Name, ManagerUser_ID)
Users(ID, Name, Active)
Delegates(ProjectID, UserID, OrderNo)
  

ManagerUser_ID является менеджером проекта для проекта и представляет собой ссылку на таблицу users. Пользователи, однако, могут быть Inactive . Итак, таблица делегатов — это таблица «многие ко многим», определяющая пользователей, которые могут получить доступ к данным проекта.

Что мне требуется, так это выбрать первого делегата для проекта, который Active предполагает, что менеджер Inactive . OrderNo Поле определяет порядок делегатов (1 является первым).

Некоторые примеры данных:

 Project
1, Project1, 2
2, Project2, 4
3, Project3, 1

Users
1, Joe, true
2, John, false
3, Dave, true
4, Bob, false

Delegates
1, 4, 1
1, 1, 2
1, 3, 3
2, 2, 1
2, 4, 2
2, 3, 3
  

Таким образом, выходные данные моего запроса должны были бы отображать:

 Project1, Joe
Project2, Dave
Project3, Joe
  

Показать проекты и пользователей для каждого проекта, где, если ManagerUser_ID неактивен, затем выберите User in Delegates с наименьшим OrderNo значением Active .

Ответ №1:

Кажется, это помогает (используя функцию window)

 SELECT  P.Name, ISNULL(U.Name, FirstDelegate.Name) AS ProjManager
FROM    Projects P LEFT OUTER JOIN
        Users U ON P.ManagerUser_ID = U.ID AND U.Active = 1 LEFT OUTER JOIN
        (
            SELECT * FROM
            (
                SELECT D.ProjectID, 
                       US.Name, 
                       ROW_NUMBER() OVER (PARTITION BY ProjectID ORDER BY OrderNo) AS SeqNo
                FROM  Delegates D INNER JOIN
                      Users US ON D.UserID = US.ID
                WHERE US.Active = 1
            ) AS Del
            WHERE Del.SeqNo = 1
        ) AS FirstDelegate ON P.ID = FirstDelegate.ProjectID
  

Ответ №2:

 Select projectName, userName
From

(
    Select projectName, userName, row_number() over (partition by projectName order by priority ASC) as rank
    From
    (
        SELECT p.name as projectName, u.name as userName, 1 as priority
        FROM projects p INNER JOIN users u ON u.active = true and u.id = p.ManagerUser_ID

        UNION

        SELECT TOP(1) p.name, u.name, 2
        FROM Delegates d INNER JOIN projects p  ON p.id = d.projectId
                         INNER JOIN users u ON u.id = d.userId
        Where u.active = true
        Order by u.OrderNo ASC
    )
)
where rank = 1
  

Ответ №3:

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

 select p.Name,m.Name from Projects p inner join Users m on p.ManagerUser_ID = m.ID and m.Active = 1
union all
select
    p.Name,
    u.Name
from
    Projects p
        inner join
    Delegates d
        on
            p.ID = d.ProjectID
        inner join
    Users u
        on
            d.UserID = u.ID and
            u.Active = 1
        left join
    Delegates d_anti
        inner join
    Users u_anti
        on
            d_anti.UserID = u_anti.ID and
            u_anti.Active = 1
        on
            p.ID = d_anti.ProjectID and
            d_anti.OrderNo < d.OrderNo
where
    u_anti.ID is null
  

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

1. Это выглядит действительно близко, я должен проверить это завтра. Однако последнее предложение ON неверно — u_anti. OrderNo < u.OrderNo — в Users нет поля OrderNo. Я предполагаю, что это d_anti. OrderNo < d.OrderNo?

Ответ №4:

 select p.name, 
u.name, 
min(d.orderNo)
from projects p, 
users u, 
users manager, 
delegates d
where p.ManagerUser_ID = manager.id
and manager.active = false
and p.id = d.projectId
and d.userid = u.id
group by p.name, 
u.name
  

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

1. К сожалению, при этом не восстанавливается Первый активный делегат для проекта. Здесь перечислены все пользователи проекта вместе с порядком. Где-то должен быть вызов, который ограничивает результирующий набор самым низким активным порядком.