[图书] isbn(PK),title,category_id,subcategory_id,price
[作者] isbn(FK),author_id(PK),姓名
[类别] category_id(PK),名称
[SubCategory] sub_category_id(PK),category_id(FK),name
我有一个包含上述四个表的数据库(不是我设计的)。
我想要一个具有以下格式的图书清单:
isbn,title,作者姓名,类别名称,子类别名称(可能没有),价格
但是有一些复杂性,正如您所看到的,每本书可以有多个作者,作者姓名列应该用逗号分隔作者名称。
对于较难部分的类别,有些类别没有子类别,因此,某些图书记录将subcategory_id设置为0,因为其category_id指的是没有子类别的类别,在这种情况下,书单中的子类别名称列不需要显示任何内容。
我真的不知道如何快速构建这样复杂的复杂SQL语句来获取图书清单。有人会想到一个解决方案吗?
非常感谢大家。
答案 0 :(得分:2)
当你发现自己构建了一个“非常复杂的SQL语句”时,通常最好退后一步并重新思考。
记住这一点 - 在数据库表上执行的绝大多数操作都是选择,而不是插入或更新(当然,每个规则都有例外)。
正在“花费”CPU周期计算诸如作者列表之类的东西的正确时间是当列表发生变化时,不是当你只想提取信息时。
将另一列添加到名为author_list的book表中,然后在authors上创建一个插入/更新触发器,以便在为特定ISBN更改作者时重建此列。
这会将成本放在应有的位置,并使您的查询更加简单。触发器可确保数据保持一致,如果您知道自己在做什么,则可以打破3NF。
对于子类别,case
语句可以是您的朋友,但是选择的每行函数永远不会很好地扩展。
我只想在子类别中创建一组id为0的行(每个类别一个),并将其名称留空。然后可以通过简单的连接完成,而不必关心性能。这也可能是在类别上带有触发器,因此每个类别的子类别总是为0。
通过这两项更改,查询变得更加复杂,类似于:
select b.isbn, b.title, b.author_list, c.name, sc.name, b.price
from Book b, Category c, SubCategory sc
where b.category_id = c.category_id
and b.category_id = sc.category_id
and b.subcategory_id = sc.subcategory_id
order by ...
这个查询应该尖叫,因为它只使用关系代数的基本级别(即,没有每行函数(包括case语句),没有子查询)。这是一个“老派”查询,您可以通过使用显式而非隐式JOIN获得更多性能。
最后一点:正确的3NF模式在authors表中没有ISBN - 更好的选择是使用一个单独的BookAuthor表来保存ISBN和author_id以正确建模多对多关系。但是你可能已经改变了性能(我不知道)。
答案 1 :(得分:1)
这是一个奇怪的架构,而不是我如何设计它。被非规范化,它可能会在作者表中有很多重复。
无论如何,因为你可能有一个或多个作者,所以加入并不会真正削减它的信息。有些事情,说实话,最好在SQL之外完成,这是其中之一。您可以构建一个构建信息的循环,并在ISBN更改时发出数据,假设您的订单很好。
对于类别和子类别,使用左连接,它将在您可以测试的子类别信息上返回NULL。如果这本书可能有多个子类别(或者那个问题的类别),那么你真的在这里使用SQL。
答案 2 :(得分:0)
这样的事情应该很接近。
select
Book.ISBN,
Book.Title,
Author.Name,
Category.Name as Category_Name,
SubCategory.Name as SubCategory_Name,
Book.Price
from
Book join Author
on Book.ISBN = Author.ISBN
join Category
on Book.Category_ID = Category.Category_ID
join SubCategory
on Book.Category_ID = SubCategory.Category_ID
and Book.SubCategory_ID = SubCategory.Sub_Category_ID
答案 3 :(得分:0)
请参阅@ Pax的答案,以便更好地处理sub_category_id的null / zero值
select isbn, a.name as author_name, c.name as category_name, sc.name as subcategory_name, price
from Book
join Author a on isbn = a.isbn
join Category c on category_id = c.category_id
join SubCategory sc on category_id = sc.category_id and subcategory_id = sc.subcategory_id
where subcategory_id != 0
union
select isbn, a.name as author_name, c.name as category_name, '' as subcategory_name, price
from Book
join Author a on isbn = a.isbn
join Category c on category_id = c.category_id
join SubCategory sc on category_id = sc.category_id and subcategory_id = sc.subcategory_id
where subcategory_id = 0
答案 4 :(得分:0)
嗯,子类别业务的数据库设计很差。即使你假设一本书只能在一个类别中,它也是一个糟糕的设计,因为(在这种情况下),一个类别总是可以从子类别派生出来,所以你通过让书具有两个属性来引入冗余。 / p>
就您想要的查询而言,这只是进行连接和预测选择语句的问题。在你不知道足够的SQL来做到这一点,你可能不应该尝试编写查询(或者你应该询问基本的连接和预测)。
关于如何将多行转换为一行(这是你想对作者做的),这取决于你的RDBMS(你没有指定)和/或你的前端。