【问题标题】:OLTP-Database designOLTP-数据库设计
【发布时间】:2014-04-24 16:33:07
【问题描述】:

我需要帮助。我有 2 个表 Books 和 Authors

  • 一本书可以有多个作者
  • 一个作者可以写多本书

所以我设计了 Mapping/Junction 表来维护这种关系

我的要求 - 我想获取给定作者组合的图书 ID、姓名。 在下面的例子中说作者 A2 和 A3 写的书 'B3' (103)。所以我的输入将是 302 和 303(A2 和 A3 id),查询应该给我 103(书 id)

如果需要,请建议架构更改

这是在 SQL Server 2005 及更高版本中工作的示例代码

declare @tbl_Books TABLE (Book_ID INT, Book_Name VARCHAR(500))
declare @tbl_Authors TABLE (Author_ID INT, Author_Name VARCHAR(50))
declare @tbl_Mapping TABLE (Mapping_ID INT IDENTITY(1,1), Book_ID INT, Author_ID INT)

insert into @tbl_Books VALUES (101,'B1'),(102,'B2'),(103,'B3')
insert into @tbl_Authors VALUES (301,'A1'),(302,'A2'),(303,'A3')
insert into @tbl_Mapping VALUES (101,301),(101,302),(102,301),(102,302),(101,303),(103,302),(103,303)

select * from @tbl_Books
select * from @tbl_Authors
select * from @tbl_Mapping

Table : tbl_Books
==========   
Book_ID Book_Name
101 B1
102 B2
103 B3

Table: tbl_Authors   
===================
Author_ID   Author_name
301 A1
302 A2
303 A3

Table:tbl_Mapping        
==============
Mapping_ID  Book_ID Author_ID
1   101 301
2   101 302
3   102 301
4   102 302
5   102 303
6   103 302
7   103 303

【问题讨论】:

  • 您想了解什么?对于这类问题,这似乎是一个很好的设计。
  • 我的要求 - 我想获取给定作者组合的图书 ID、姓名,如何通过使用此架构或需要更改架构设计来实现此目的?

标签: database


【解决方案1】:

这不是很漂亮,但它有效:

SELECT x.book_id, b.book_name 
FROM (SELECT book_id, COUNT(*) AS num FROM tbl_mapping GROUP BY book_id) x  --Get all books with a count of their authors
INNER JOIN (SELECT book_id FROM tbl_mapping WHERE author_id IN (302,303)) y  --Get all books which involve the specified authors
    ON y.book_id = x.book_id
INNER JOIN tbl_books b
    ON b.book_id = x.book_id
WHERE x.num = 2  --Filter for books which have exactly the required number of authors
GROUP BY x.book_id, b.book_name 
HAVING COUNT(*) = 2  --Filter for how many times each book appears in the results. We want those that appear as many times as there are authors being searched

为了使其不那么静态,您必须以某种方式根据您提供的作者 ID 列表构建您的 IN 子句,其中显示 = 2 您需要将 2 更改为数字作者被搜索。

我通过将另一本书添加到仅由一位作者编写的示例数据并相应地调整查询来对其进行测试。它返回了我的预期。还与三位作者一起尝试了这本书,这本书也很有效。这几乎不构成稳健的测试,但它证明了基本概念。我敢肯定有更好的方法可以使用窗口函数来做到这一点,但坦率地说,这是我的晚餐时间,我很饿,所以我想不出!

【讨论】:

  • 另外,映射表上的ID列对需求来说是多余的。
  • 之所以如此痛苦,是因为您只想退回您指定的两位作者所写的书,而不是任何他们都参与过的书(可能和其他人)。老实说,无论您做什么设计,搜索都可能比另一种方式更复杂。挑书找作者容易,找作者全部或部分写的书容易,这是难点。另一种设计可能会使这更容易,而其他搜索则更难。
  • 是的,史蒂夫,我同意你的观点 在这个设计中搜索作者组合来获取书籍是很困难的 是的,我正在寻找一个更好的设计来进行高效查询
  • 好吧,我想您可能会发现,您可以找到的每个设计在某些搜索中都有其缺点。对此的查询计划实际上并不算太糟糕(SQL Server),但是您当然必须确保适当地设计索引。如果要在主键上搜索聚簇索引,并酌情在其他地方搜索 NC 索引,请在主键上使用聚簇索引。不过,这完全是另一场辩论。
【解决方案2】:

因此,您正在寻找一组给定作者的图书 ID 和图书名称。

你可以尝试类似(非常伪sql):

select tb.Book_ID, tb.Book_Name, SUM(tm.Author_ID) as authors FROM tbl_Mapping tm
       INNER JOIN tbl_Book tb on tb.Book_ID = tm.Book_ID
       WHERE tm.Author_ID IN ( <your list of authors>)
       AND authors = (<the number of authors passed in>)
       GROUP BY tb.Book_ID

但我不确定作者别名作为过滤器的合法性(我从未真正在 SQL 中直接这样做过)

然而,一种程序化方法是有如下查询:

select Book_ID from tbl_Mapping WHERE Author_ID = <One author ID>

并把它放在一个循环中。上面的查询是第一次执行,后面的查询也有

AND BOOK_ID IN (<List of Book IDs returned by previous loops)

循环直到作者用完,然后通过查询运行这些 ID 以获取名称(或者将名称附加到先前的查询并跟踪它)。

【讨论】:

  • 您不能在 WHERE 子句中使用聚合,这样就不会起作用。
  • 感谢您的回复我真的不确定我是否理解您的回答我们如何在没有 GROUP BY 的情况下拥有聚合子句?意图不仅仅是查询,我真的很想知道我的架构设计是否足以满足这个要求?
  • 更新了一些东西。我认为架构很好。如果您无法在一个查询中完全实现这一点,您可以轻松地在应用程序中执行一些程序化操作以获得您想要的结果。
  • COUNT 是一个聚合函数,它可能不会出现在 WHERE 子句中:聚合可能不会出现在 WHERE 子句中,除非它出现在 HAVING 子句中包含的子查询中或选择列表,并且被聚合的列是外部引用
  • 这仍然行不通(至少在 SQL Server 和其他可能也是如此),因为 authors 正如您所指出的那样,它不是过滤的有效列。我的方法类似,但由于我在子查询中进行计数,因此可以在外部将其作为过滤器引用。除此之外,它是相似的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多