我们如何构建这样一个极其复杂的SQL语句?

时间:2009-10-06 01:54:23

标签: sql mysql

[图书] 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语句来获取图书清单。有人会想到一个解决方案吗?

非常感谢大家。

5 个答案:

答案 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(你没有指定)和/或你的前端。