У меня нет лучшего названия для этого, и я уже нашел решение - я хочу знать, есть ли более элегантный способ решить эту проблему, поскольку мне не очень нравится то, как я это сделал.
Проблема в том, что у меня есть несколько предметов (назовем их людьми), которые находятся в 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')
Спасибо - и, возможно, кто-то может предложить лучшее название для этого вопроса.