Запрос данных из разных тем в один набор результатов в T-SQL - PullRequest
0 голосов
/ 27 августа 2018

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

Проблема в том, что у меня есть несколько предметов (назовем их людьми), которые находятся в m: n-отношениях с двумя разными вещами (назовем их отделами и ассоциациями). Давайте предположим, что Лицо 1 является членом Департаментов 1 и 2 и Ассоциации 1, Лицо 2 является членом Департамента 3 и Ассоциаций 1, 2 и 3, а Лицо 3 является членом Департаментов 1, 2 и 4 и Ассоциации 3. То, что я хочу, это результирующий набор, который выглядит как

+==========+==============+===============+
| Person   | Department   | Association   |
+==========+==============+===============+
| Person 1 | Department 1 | Association 1 |
+----------+--------------+---------------+
| Person 1 | Department 2 | NULL          |
+----------+--------------+---------------+
| Person 2 | Department 3 | Association 1 |
+----------+--------------+---------------+
| Person 2 | NULL         | Association 2 |
+----------+--------------+---------------+
| Person 2 | NULL         | Association 3 |
+----------+--------------+---------------+
| Person 3 | Department 1 | Association 3 |
+----------+--------------+---------------+
| Person 3 | Department 2 | NULL          |
+----------+--------------+---------------+
| Person 3 | Department 4 | NULL          |
+----------+--------------+---------------+

Что я сделал:

Сначала я создал 5 таблиц:


    CREATE TABLE Persons(
       PersonID int IDENTITY(1,1) NOT NULL,
       PersonName nvarchar(50) NOT NULL,
       CONSTRAINT PK_Persons PRIMARY KEY CLUSTERED (PersonID ASC)
    )

    CREATE TABLE Associations(
       AssociationID int IDENTITY(1,1) NOT NULL,
       AssociationName nvarchar(50) NOT NULL,
       CONSTRAINT PK_Associations PRIMARY KEY CLUSTERED (AssociationID ASC)
    )

    CREATE TABLE Departments(
       DepartmentID int IDENTITY(1,1) NOT NULL,
       DepartmentName nvarchar(50) NOT NULL,
       CONSTRAINT PK_Departments PRIMARY KEY CLUSTERED (DepartmentID ASC)
    )

    CREATE TABLE AssociationMembers(
       PersonID int NOT NULL,
       AssociationID int NOT NULL,
       CONSTRAINT PK_AssociationMembers PRIMARY KEY CLUSTERED (PersonID ASC, AssociationID ASC)
    )

    CREATE TABLE DepartmentMembers(
       PersonID int NOT NULL,
       DepartmentID int NOT NULL,
       CONSTRAINT PK_DepartmentMembers PRIMARY KEY CLUSTERED (PersonID ASC, DepartmentID ASC)
    )

Затем я вставил некоторые данные:


    INSERT INTO Persons(PersonName) VALUES('Person 1')
    INSERT INTO Persons(PersonName) VALUES('Person 2')
    INSERT INTO Persons(PersonName) VALUES('Person 3')
    INSERT INTO Associations(AssociationName) VALUES ('Association 1')
    INSERT INTO Associations(AssociationName) VALUES ('Association 2')
    INSERT INTO Associations(AssociationName) VALUES ('Association 3')
    INSERT INTO Departments(DepartmentName) VALUES ('Department 1')
    INSERT INTO Departments(DepartmentName) VALUES ('Department 2')
    INSERT INTO Departments(DepartmentName) VALUES ('Department 3')
    INSERT INTO Departments(DepartmentName) VALUES ('Department 4')
    INSERT INTO AssociationMembers(PersonID, AssociationID) VALUES (1, 1)
    INSERT INTO AssociationMembers(PersonID, AssociationID) VALUES (2, 1)
    INSERT INTO AssociationMembers(PersonID, AssociationID) VALUES (2, 2)
    INSERT INTO AssociationMembers(PersonID, AssociationID) VALUES (2, 3)
    INSERT INTO AssociationMembers(PersonID, AssociationID) VALUES (3, 3)
    INSERT INTO DepartmentMembers(PersonID, DepartmentID) VALUES (1, 1)
    INSERT INTO DepartmentMembers(PersonID, DepartmentID) VALUES (1, 2)
    INSERT INTO DepartmentMembers(PersonID, DepartmentID) VALUES (2, 3)
    INSERT INTO DepartmentMembers(PersonID, DepartmentID) VALUES (3, 1)
    INSERT INTO DepartmentMembers(PersonID, DepartmentID) VALUES (3, 2)
    INSERT INTO DepartmentMembers(PersonID, DepartmentID) VALUES (3, 4)

и создал два вида:


    CREATE VIEW v_AssociationMemberships
    AS
    SELECT
       p.PersonID,
       ROW_NUMBER() OVER (PARTITION BY p.PersonID ORDER BY a.AssociationID) AS 'AssociationRow',
       p.PersonName,
       a.AssociationID,
       a.AssociationName
    FROM
       dbo.Persons p
       INNER JOIN dbo.AssociationMembers am ON am.PersonID = p.PersonID
       INNER JOIN dbo.Associations a ON a.AssociationID = am.AssociationID

    CREATE VIEW v_DepartmentMemberships
    AS
    SELECT
       p.PersonID,
       ROW_NUMBER() OVER (PARTITION BY p.PersonID ORDER BY d.DepartmentID) AS 'DepartmentRow',
       p.PersonName,
       d.DepartmentID,
       d.DepartmentName
    FROM
       dbo.Persons p
       INNER JOIN dbo.DepartmentMembers dm ON dm.PersonID = p.PersonID
       INNER JOIN dbo.Departments d ON d.DepartmentID = dm.DepartmentID

Вот так выглядит последний запрос, и - он работает, он делает то, что должен, но мне интересно, есть ли более простой и более элегантный способ сделать это (используя все, что может предложить T-SQL):


    SELECT
       ISNULL(t.aPersonName, t.dPersonName) AS PersonName,
       t.dDepartmentName AS DepartmentName,
       t.aAssociationName AS AssociationName
    FROM (
       SELECT
          d.PersonName AS dPersonName,
          d.DepartmentName AS dDepartmentName,
          a.PersonName AS aPersonName,
          a.AssociationName AS aAssociationName
       FROM
          dbo.v_DepartmentMemberships d
          RIGHT OUTER JOIN dbo.v_AssociationMemberships a ON a.PersonID = d.PersonID AND a.AssociationRow = d.DepartmentRow
       UNION
       SELECT
          d.PersonName AS dPersonName,
          d.DepartmentName AS dDepartmentName,
          a.PersonName AS aPersonName,
          a.AssociationName AS aAssociationName
       FROM
          dbo.v_AssociationMemberships a
          RIGHT OUTER JOIN dbo.v_DepartmentMemberships d ON d.PersonID = a.PersonID AND d.DepartmentRow = a.AssociationRow
    ) t
    ORDER BY
       PersonName,
       ISNULL(t.dDepartmentName, 'zzzzzzzzzzzzz'),
       ISNULL(t.aAssociationName, 'zzzzzzzzzzzzz')

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

1 Ответ

0 голосов
/ 27 августа 2018

В вашем запросе есть таблицы, которые не связаны друг с другом - Ассоциации и Отделы - и вы не хотите, чтобы результат имел перекрестное произведение двух таблиц. Поэтому вам нужно создать новые таблицы, представления, подзапросы или CTE, которые связывают две таблицы таким образом, чтобы на одного человека отображалось только одно значение.

Я думаю, что ваша идея использовать ROW_NUMBER была правильной идеей, но вы обошли ее кругом. В этом ответе я использую CTE для добавления уникального ROW_NUMBER к Ассоциациям и Отделам для каждого члена, связанного с человеком, а затем использую другой CTE для создания набора результатов, используемого для объединения Ассоциаций и Отделов с Лицами.

WITH am AS (
    SELECT PersonID, am.AssociationID
        , ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY am.AssociationID) PersonRowID
    FROM AssociationMembers am
)
, dm AS (
    SELECT PersonID, dm.DepartmentID
        , ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY dm.DepartmentID) PersonRowID
    FROM DepartmentMembers dm
)
, personRows AS (
    SELECT PersonID, PersonRowID FROM am 
    UNION 
    SELECT PersonID, PersonRowID FROM dm
)
SELECT p.PersonName, d.DepartmentName, a.AssociationName
FROM personRows
INNER JOIN Persons p ON p.PersonID = personRows.PersonID
LEFT JOIN am ON am.PersonID = p.PersonID AND am.PersonRowID = personRows.PersonRowID
LEFT JOIN Associations a ON a.AssociationID = am.AssociationID
LEFT JOIN dm ON dm.PersonID = p.PersonID AND dm.PersonRowID = personRows.PersonRowID
LEFT JOIN Departments d ON d.DepartmentID = dm.DepartmentID

personRows CTE использует тот факт, что UNION будет сохранять только отдельные значения, так что PersonRowID для каждого человека будет иметь одно уникальное значение вплоть до макс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...