意简言赅,这一课介绍什么是子查询,如何使用它们。
早学完,早睡觉。困极了。
目录
11.1 子查询
查询(query)任何SQL语句都是查询。但此术语一般指SELECT语句。
目前学习的所有SELECT语句都是简单查询,即从单个数据库表中检索数据的单条语句。
子查询(subquery),即嵌套在其他查询中的查询。
讲到子查询,应该聊聊MySQL支持,对子查询的支持是从4.1版本引入的(面试可能考!注意)。MySQL的早期版本不支持子查询。
11.2 利用子查询进行过滤
主要研究的对象是关系表,订单存储在两个表中。每个订单包含订单编号、客户ID、订单日期,在Orders表中存储为一行。各订单的物品存储在相关的OrderItems表中。Orders表不存储顾客信息,只存储顾客ID。顾客的实际信息存储在Customers表中。
假使麻烦来了,需要列出订购物品RGAN01的所有顾客,怎么搞呢?
- 检索包含物品RGAN01的所有订单的编号。
- 检索具有前一步骤列出的订单编号的所有顾客的ID。
- 检索前一步骤返回的所有顾客ID的顾客信息。
起码要分这散步吧,拿到订单,拿到顾客,拿到顾客的信息才算结束。那么这三步检索,能不能把某条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句呢?
来试试,先说好,分别练习的语句不放上来,只放截图,最后综合句一起放:
SELECT cust_id FROM Orders WHERE order_num IN (SELECT order_num FROM OrderItems WHERE prod_id = 'RGAN01');
在SELECT语句中,子查询总是从内向外处理。那么考虑内部查询,外部查询的话,可以看左图,上面的为右图的内部查询,下面的为外部查询。输出一样,返回值相同。
另补一句:格式化SQL。说拿颜色区别开来,将子查询分解成多行缩进,看的清晰明了。
现在得到了订购物品RGAN01的所有顾客的ID。下一步是检索这些顾客ID的顾客信息。也就是说,工作还没干完,但是是个好兆头,接着做剩下的部分:
拿前面的语法也能做,得到结果,over~停!我们这一课学的子查询,干啥不把三个一起呢???
接着来。
SELECT cust_name,cust_contact FROM Customers
WHERE cust_id IN(SELECT cust_id FROM Orders
WHERE order_num IN (SELECT order_num FROM OrderItems
WHERE prod_id = 'RGAN01'));
终于挖出最后的结果,并且和上面得到结果一致。
在WHERE子句中使用子查询能够编写出功能很强且很灵活的SQL语句。对于能嵌套的子查询的数目没有限制,不过在实际使用时由于性能的限制,不能嵌套太多的子查询。
子查询和性能这里给出的代码有效,并且获得了所需的结果。但是,使用子查询并不总是执行这类数据检索的最有效方法。往后看就知道了,下一课就不太行。
11.3 作为计算字段使用子查询
使用子查询的另一方法是创建计算字段。
比如说需要显示Customers表中每个顾客的订单总数。订单与相应的顾客ID存储在Orders表中。
那么为了拿到需要的结果,我们要:
(1)从Customers表中检索顾客列表;
(2)对于检索出的每个顾客,统计其在Orders表中的订单数目。
统计数目好说,SELECT COUNT(*)对表中的行进行计数,还可以通过提供一条WHERE子句来过滤某个特定的顾客ID,仅对该顾客的订单进行计数。
SELECT cust_name,cust_state, (SELECT COUNT(*) FROM Orders WHERE Orders.cust_id = Customers.cust_id) AS orders FROM Customers ORDER BY cust_name;
这条SELECT语句对Customers表中每个顾客返回三列:cust_name、cust_state和orders。orders是一个计算字段,它是由圆括号中的子查询建立的。该子查询对检索出的每个顾客执行一次。在此例中,该子查询执行了5次,因为检索出了5个顾客。
(哈欠连天的我也忙不上给这段分析加注释了,直接copy上来了,主要是对于订单中顾客计数,然后再去检索出顾客信息)
用一个句点分隔表名和列名,在有可能混淆列名时必须使用这种语法。
子查询中的WHERE子句与前面使用的WHERE子句稍有不同,因为它使用了完全限定列名,而不只是列名(cust_id)。它指定表名和列名(Orders.cust_id和Customers.cust_id)。也就是在讲,上面不是Orders.cust_id吗?是避免跟Customers表中的搞混了。
然后书上为了验证这样干(没有前面表名起作用的完全限定名)是不好滴,得不到想要的结果,做了一个小测试:
看看,多么痛的领悟~拿到的数据完全不一样,简直崩溃!
虽然子查询在构造这种SELECT语句时有用,但必须注意限制有歧义的列。不要搞二义性这种东西。
如果在SELECT语句中操作多个表,就应使用完全限定列名(带个表名,做个人吧)来避免歧义。
不止一种解决方案,虽然为了熟悉语法,我们强行将很多样例代码都运行出来了,但是这不代表,这些就是解决数据检索的最优方法,后面学习,还会遇到更多问题更多组合解决方案,我们应当因地制宜,选择合适的解决方案。
这节课就在我的哈欠和不停得得得~中度过去了。因为笔记本电脑的鼠标“C”键一直自己按按按,家人又有需要,所以把笔记本和那个酷似网吧键盘的blingbling白键盘贡献出去了,配合上次入手的白鼠标,一套好看极了。
可没了键程舒适按压力道刚好的键盘的我,打字都不香了。这并不是我今天不学三课的原因。
明天见,刻意练习,每日精进。晚安。