我们如何构建如此复杂的SQL语句?

我们如何构建如此复杂的SQL语句?,sql,mysql,Sql,Mysql,[图书]isbn(主键),标题,类别id,子类别id,价格 [作者]isbn(FK)、作者id(PK)、姓名 [类别]类别id(主键),名称 [子类别]子类别id(PK)、类别id(FK)、名称 我有一个包含上述四个表的数据库(不是我设计的) 我想要一个具有以下格式的图书列表: isbn、标题、作者姓名、类别名称、子类别名称(可能没有)、价格 但也有一些复杂性,正如您所看到的,每本书可以有多个作者,作者姓名列应该有作者姓名,并用逗号分隔 对于更难的类别,有些类别没有子类别,因此有些图书记录的子类

[图书]isbn(主键),标题,类别id,子类别id,价格

[作者]isbn(FK)、作者id(PK)、姓名

[类别]类别id(主键),名称

[子类别]子类别id(PK)、类别id(FK)、名称

我有一个包含上述四个表的数据库(不是我设计的)

我想要一个具有以下格式的图书列表:

isbn、标题、作者姓名、类别名称、子类别名称(可能没有)、价格

但也有一些复杂性,正如您所看到的,每本书可以有多个作者,作者姓名列应该有作者姓名,并用逗号分隔

对于更难的类别,有些类别没有子类别,因此有些图书记录的子类别id设置为0,因为其类别id指的是没有子类别的类别,在这种情况下,图书列表中的子类别名称列不需要显示任何内容

我真的不知道如何快速构建如此复杂的SQL语句来获取图书列表。有人能想出一个解决办法吗


非常感谢大家。

像这样的事情应该很接近了

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

像这样的事情应该很接近

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

当您发现自己正在构建一个“极其复杂的SQL语句”时,通常最好退一步重新思考

记住这一点——对数据库表执行的绝大多数操作都是选择,而不是插入或更新(当然,每个规则都有例外)

“花费”CPU周期计算诸如作者列表之类的内容的正确时间是在列表更改时,而不是在您只想提取信息时


将另一列添加到图书表中,称为author_list,然后在authors上创建一个insert/update触发器,以便在为特定ISBN更改作者时重新生成此列

这将使成本达到应有的水平,并使您的查询更加简单。触发器确保数据保持一致,如果您知道自己在做什么,可以中断3NF

至于子类别,
case
语句可以是您的朋友,但select上的每行函数永远无法很好地伸缩

我只需要在子类别中创建一组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语句),没有子查询)。这是一个“老派”的查询,通过使用显式连接而不是隐式连接,您可能会获得更高的性能


最后一点:一个正确的3NF模式不会在authors表中包含ISBN——更好的选择是使用一个单独的BookAuthor表来保存ISBN和author_id,从而正确地建模多对多关系。但是为了性能,您可能已经改变了这一点(我不知道)。

当您发现自己正在构建一个“极其复杂的SQL语句”时,通常最好是退一步重新思考

记住这一点——对数据库表执行的绝大多数操作都是选择,而不是插入或更新(当然,每个规则都有例外)

“花费”CPU周期计算诸如作者列表之类的内容的正确时间是在列表更改时,而不是在您只想提取信息时


将另一列添加到图书表中,称为author_list,然后在authors上创建一个insert/update触发器,以便在为特定ISBN更改作者时重新生成此列

这将使成本达到应有的水平,并使您的查询更加简单。触发器确保数据保持一致,如果您知道自己在做什么,可以中断3NF

至于子类别,
case
语句可以是您的朋友,但select上的每行函数永远无法很好地伸缩

我只需要在子类别中创建一组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语句),没有子查询)。这是一个“老派”的查询,通过使用显式连接而不是隐式连接,您可能会获得更高的性能


最后一点:一个正确的3NF模式不会在authors表中包含ISBN——更好的选择是使用一个单独的BookAuthor表来保存ISBN和author_id,从而正确地建模多对多关系。但是为了性能,您可能已经修改了aleready(我不知道)。

请参阅@Pax的答案,了解处理sub_category_id的空/零值的更好方法

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

请参阅@Pax的答案,了解处理sub_category_id的空/零值的更好方法

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

嗯,子类别业务是糟糕的数据库设计。即使您假设一本书只能属于一个类别,这也是一个糟糕的设计,因为(在这种情况下),一个类别总是可以派生自子类别,因此您通过让书同时具有这两个类别的属性引入了冗余

对于所需的查询,这只是执行联接和投影select语句的问题。如果您不知道足够的SQL来实现这一点,您可能不应该尝试编写