如何从多对多表中选择一对一关系

Posted

技术标签:

【中文标题】如何从多对多表中选择一对一关系【英文标题】:How to select one to one relations from many to many table 【发布时间】:2016-03-07 12:48:20 【问题描述】:

我有一个数据库,用于保存书籍及其作者。在我的模型中,一个作者可以有很多本书,而一本书可以由多个作者写。示例:

AUTHOR_ID|BOOK_ID
1|100
1|200
1|300
2|300
3|300
4|400

我正在努力寻找只写了一本书的作者,而那本书只能由该作者本人撰写。在上面的例子中,只有有效的作者是 AUTHOR_ID = 4。 我需要编写一个选择来获取满足上述要求的作者 ID,我该如何编写快速高效的选择来做到这一点?

【问题讨论】:

【参考方案1】:
select *
from BookAuthors t1
where not exists (select * from BookAuthors t2
                  where t2.BOOK_ID = t1.BOOK_ID
                    and t2.Author_ID <> t1.Author_ID)
  and not exists (select * from BookAuthors t3
                  where t3.Author_ID  = t1.Author_ID
                    and t3.BOOK_ID <> t1.BOOK_ID)

第一个NOT EXISTS 是为了确保同一个bookid 没有第二个作者。

第二个NOT EXISTS 用于确保相同的 Author_ID 没有写过另一本书。

组合版:

select *
from BookAuthors t1
where not exists (select * from BookAuthors t2
                  where (t2.BOOK_ID = t1.BOOK_ID
                         and t2.Author_ID <> t1.Author_ID)
                     or (t2.Author_ID  = t1.Author_ID
                         and t2.BOOK_ID <> t1.BOOK_ID))

【讨论】:

【参考方案2】:

这是一个单独的答案,因为第一个是错误的。

如果你的数据库支持窗口函数,一种方法是:

select Author_ID
from (select ba.*, count(*) over (partition by Author_ID) as numBooks,
             count(*) over (partition by Book_ID) as numAuthors
      from BookAuthors ba
     ) ba
where numBooks = 1 and numAuthors = 1;

通过将条件移至where 子句或joins 也有多种方法:

select ba.*
from BookAuthors ba
where Author_ID in (select Author_Id from BookAuthors group by Author_Id having count(*) = 1) and
      Book_ID in (select Author_Id from BookAuthors group by Book_ID having count(*) = 1);

【讨论】:

以上是关于如何从多对多表中选择一对一关系的主要内容,如果未能解决你的问题,请参考以下文章

多表查询

web框架开发-Django模型层-多表操作

MySQL 基础 -- 多表关系(一对一1对多(多对一)多对多)多表查询(内连接外连接自连接子查询(嵌套查询)联合查询 union)笛卡儿积

多表查询

Django之模型层-多表操作

数据库表关系及配置