多对多关系

som*_*ser 4 sqlite join aggregate

我正在研究图书数据库。除其他外,我有以下两个表(针对问题进行了一些简化):

tblBook: ID, Title
tblPerson: ID, Name
Run Code Online (Sandbox Code Playgroud)

请注意,该tblPerson表包含作者、翻译者和编辑(不仅是作者)。

很明显,这两个表是多对多的关系,所以我也有如下联结表:

tblBookAuthorJunction
tblBookTranslatorJunction
tblBookEditorJunction
Run Code Online (Sandbox Code Playgroud)

但是当涉及到加入表以选择所有角色时,我遇到了一个问题:如何创建一个查询以获得一个看起来像这样的表:

BookTitle AuthorName TranslatorName EditorName
Run Code Online (Sandbox Code Playgroud)

这是可能吗?我的查询应该是什么样的?

编辑:您可以为 RDBMS 假设 SQLite。


更新

到目前为止,有两个包含查询的答案。我想将这些答案与可能会发现此信息有价值的其他人进行比较...请注意,连接表的名称与问题中的名称略有不同。而且,是的,我知道,测试数据很无聊。

答案 1

我自己的回答中的查询如下:

SELECT B.BookTitle, P1.PersonName, P2.PersonName, P3.PersonName
FROM tblBook B
LEFT JOIN tblBookAuthor A ON B.BookID = A.BookID
LEFT JOIN tblPerson P1 ON A.AuthorID = P1.PersonID
LEFT JOIN tblBookTranslator T ON B.BookID = T.BookID
LEFT JOIN tblPerson P2 ON T.TranslatorID = P2.PersonID
LEFT JOIN tblBookEditor E ON B.BookID = E.BookID
LEFT JOIN tblPerson P3 ON E.EditorID = P3.PersonID
Run Code Online (Sandbox Code Playgroud)

此查询的输出如下所示:
在此处输入图片说明

答案 2

Erwin Brandstetter 的回答中的查询如下(稍微修改以适应测试数据库):

SELECT B.BookTitle, BA.Authors, BT.Translators, BE.Editors
FROM tblBook B
LEFT JOIN (
    SELECT  J.BookID, group_concat(P.PersonName, ', ') AS Authors
    FROM    tblBookAuthor J
    JOIN    tblPerson P ON J.AuthorID = P.PersonID
    GROUP   BY J.BookID
    ) AS BA ON BA.BookID = B.BookID

LEFT JOIN (
    SELECT  J.BookID, group_concat(P.PersonName, ', ') AS Translators
    FROM    tblBookTranslator J
    JOIN    tblPerson P ON J.TranslatorID = P.PersonID
    GROUP   BY J.BookID
    ) AS BT ON BT.BookID = B.BookID

LEFT JOIN (
    SELECT  J.BookID, group_concat(P.PersonName, ', ') AS Editors
    FROM    tblBookEditor J
    JOIN      tblPerson P ON J.EditorID = P.PersonID
    GROUP   BY J.BookID
    ) AS BE ON BE.BookID = B.BookID
Run Code Online (Sandbox Code Playgroud)

此查询的输出如下所示:
在此处输入图片说明

Erw*_*ter 6

如果每本书可以有多个作者/编辑/翻译者 - 就像在现实生活中以及您的关系设计所建议的那样,那么带有普通LEFT JOINs的现有答案将产生不正确的结果。如果任何一本书中每个角色最多有一个人,您就可以从根本上简化您的设计:不需要连接表,只需表中的一个外键列Books

您需要聚合作者、翻译和编辑。您可以在连接所有行(并生成多个结果行)之后执行此操作,但在子查询中聚合然后连接到Books表应该更有效

SELECT B.BookTitle, BA.Authors, BT.Translators, BE.Editors
FROM   tblBook B
LEFT JOIN (
    SELECT  J.BookID, group_concat(P.Name, ', ') AS Authors
    FROM    tblBookAuthorJunction J
    JOIN    tblPerson P ON J.AuthorID = P.PersonID
    GROUP   BY J.BookID
    ) AS BA ON BA.BookID = B.ID

LEFT JOIN (
    SELECT  J.BookID, group_concat(P.Name, ', ') AS Translators
    FROM    tblBookTranslatorJunction J
    JOIN    tblPerson P ON J.TranslatorID = P.PersonID
    GROUP   BY J.BookID
    ) AS BT ON BT.BookID = B.ID

LEFT JOIN (
    SELECT  J.BookID, group_concat(P.Name, ', ') AS Editors
    FROM    tblBookEditorJunction J
    JOIN    tblPerson P ON J.EditorID = P.PersonID
    GROUP   BY J.BookID
    ) AS BE ON BE.BookID = B.ID
WHERE  B.ID = 123
Run Code Online (Sandbox Code Playgroud)

在大多数 RDBMS 中,如果您也添加WHERE BookID = 123到子查询,它会更快。