数据分析师必须掌握的数据库语言-SQL指南第五期
使用子查询进行过滤
SELECT语句用于SQL中的查询。之前使用的所有SELECT语句都是简单查询,即从单个数据库表中检索数据的单个语句。但是,SQL也可以创建子查询,即嵌套在其他查询中的查询。
示例:
数据表:这次使用的数据库表都是关系表。订单存储在两个表中。每个订单都包含订单号、客户ID和订单日期,并作为一行存储在Orders表中。每个订单的项目都存储在相关的OrderItems表中。Orders表不存储客户信息,只存储客户ID。实际的客户信息存储在Customers表中。
如果现在需要检索所有订购RGAN01的客户,应该如何检索?步骤如下:
①检索包含RGAN01项目的所有订单的编号。
②检索上一步中列出的订单号的所有客户的id。
③检索上一步返回的所有客户id的客户信息。
上述每个步骤都可以作为一个查询独立执行。
可以将一个SELECT语句返回的结果用于另一个SELECT语句的WHERE子句,也可以使用子查询将三个查询合并到一个语句中。
①第一条语句的含义很清楚,即对于所有prod_id为RGAN01的订单项,检索order_num列。
分析:通过这个语句,我们知道哪个订单包含要检索的项目。
②接下来,查询与从上述语句中检索到的订单20007和20008相关的客户ID。这里可以使用IN子句。
可以将下面两个查询组合起来,将第一个查询变成一个子查询。
分析:在SELECT语句中,子查询总是由内向外处理。在处理上述SELECT语句时,DBMS实际上执行两个操作。
首先,执行括号()中的查询,该查询返回两个订单号:20007和20008。
然后,这两个值以IN运算符要求的逗号分隔格式传递给外部查询的WHERE子句。外部查询变成:
该语句检索的结果与前面硬编码的WHERE子句返回的结果相同。
③订购产品RGAN01的所有客户id均从上述语句中获得:100004和100005。接下来,检索这些客户id的客户信息。
还可以将WHERE子句转换成子查询,这样就不必对这些客户id进行硬编码。
分析:DBMS实际上要执行三条SELECT语句才能完成上述语句。最里面的子查询返回序号,该序号用在外部子查询的WHERE子句中。外部子查询返回客户ID列,该列用在最外层查询的WHERE子句中。最外层的查询返回最终所需的数据。
可以看出,在WHERE子句中使用子查询可以写出强大灵活的SQL语句。可以嵌套的子查询数量没有限制,但在实际应用中,由于性能限制,不宜嵌套过多的子查询。
注意:作为子查询的SELECT语句只能查询一列,检索多列将返回错误。此外,使用子查询并不总是执行这种数据检索的最有效方式。
将子查询用作计算字段
使用子查询的另一种方法是创建计算字段。
示例:您需要显示Customers表中每个客户的订单总数。订单和相应的客户id存储在Orders表中。为此,您需要以下步骤:
①从客户表中检索客户列表。
②对于检索到的每个客户,统计Orders表中的订单数。
在这里,我们可以使用前面介绍的SELECT COUNT(*)对表中的行进行计数,并通过WHERE子句筛选特定的客户ID,只对该客户的订单进行计数。
统计客户100001的订单如下:
要对每个客户执行COUNT(*),需要将其用作子查询,如下所示:
分析:该SELECT语句为Customers表中的每个客户返回三列:cust_name、cust_state和orders。Orders是一个计算字段,由括号中的子查询建立。该子查询针对检索到的每个客户执行一次。在本例中,子查询执行了五次,因为检索了五个客户。
子查询中的WHERE子句与前面的WHERE子句略有不同,因为它使用完全限定的列名,而不仅仅是列名(cust_id)。它指定表名和列名(Orders.cust_id和Customers.cust_id)。下面的WHERE子句告诉SQL将Orders表中的cust_id与当前从Customers表中检索的cust_id进行比较:
当列名容易混淆时,必须用句点将表名和列名分开。在这个例子中,有两个cust_id列:一个在Customers中,另一个在Orders中。如果没有完全限定名,DBMS会认为应该比较Orders表中的cust_id本身。因为:
上面的语句总是返回Orders表中的订单总数,这不是我们想要的,如下所示:
从上面可以看出,在构造语句时,如果涉及多个表,并且没有区分相同的列名,DBMS就会抛出错误信息。
在SELECT语句中操作多个表时,使用完全限定的列名来避免歧义是一个很好的做法。
最后,总结子查询的特点:
①子查询必须用括号括起来。
②子查询的SELECT子句中只能有一列,除非主查询中有多列,用于与子查询选择的列进行比较。
③子查询不能使用ORDER BY,但主查询可以。在子查询中,GROUP BY可以起到与ORDER BY相同的作用。
④返回多行数据的子查询只能用于多值运算符,如IN运算符。
⑤选择列表不能包含对BLOB、ARRAY、CLOB或NCLOB类型值的任何引用。
⑥子查询不能直接用于聚合函数。
⑦ BETWEEN运算符不能用于子查询,但可以用于子查询。
这个介绍就到此为止。下一篇文章将解释连接和高级连接的使用。
下次见!