复杂查询

子查询

SQL 还允许创建子查询(subquery),即嵌套在其他查询中的查询。

利用子查询过滤

订单存储在两个表中。对于包含订单号、客户 ID、订单日期的每个订单,orders 表存储一行。各订单的物品存储在相关的 orderitems 表中。orders 表不存储客户信息。它只存储客户的 ID。实际的客户信息存储在 customers 表中。

假如需要列出订购物品 TNT2 的所有客户,需要下面几步:

  1. 检索包含物品 TNT2 的所有订单的编号。
  2. 检索具有前一步骤列出的订单编号的所有客户的 ID。
  3. 检索前一步骤返回的所有客户ID的客户信息。
# 检索包含物品 TNT2 的所有订单的编号
mysql> select order_num from orderitems where pod_id= 'TNT2';
+-------------+
| order_num   |
+-------------+
|      2005   |
|      2007   |
+-------------+

# 查询具有订单 20005 和 20007 的客户 ID
mysql> select cust_id from orders where order_num in (2005,2007);
+-------------+
|   cust_id   |
+-------------+
|      1001   |
|      1004   |
+-------------+

把第一个查询(返回订单号的那一个)变为子查询组合两个查询:

mysql> select cust_id from orders where order_num in (select order_num from orderitems where pod_id= 'TNT2');
+-------------+
|   cust_id   |
+-------------+
|      1001   |
|      1004   |
+-------------+

子查询总是从内向外处理

进一步根据客户 id 查出客户信息:

select cust_name from customers where cust_id in (
select cust_id from orders where order_num in (select order_num from orderitems where pod_id= 'TNT2'));

对于能嵌套的子查询的数目没有限制,但是不要嵌套太多的子查询,会影响性能。 where 子句中使用子查询,应该保证 select 语句具有与 where 子句中的列必须匹配

作为计算字段使用子查询

子查询的另一方法是创建计算字段。

假如需要显示 customers 表中每个客户的订单总数。订单与相应的客户 ID 存储在 orders 表中。

select cust_name, (select COUNT(*) from orders where orders.cust_id = customers.cust_id) as orders from customers order by cust_name;

orders 是一个计算字段,它是由圆括号中的子查询建立的。该子查询对检索出的每个客户执行一次。子查询中的 where 子句使 用了完全限定列名

联结表

join 表。

关系表

相同数据出现多次决不是一件好事,此因素是关系数据库设计的基础。关系表的设计就是要保证把信息分解成多个表,一类数据一个表。各表通过某些常用的值(即关系设计中的关系(relational))互相关联

创建联结

select vend_name, prod_name, prod_price from vendors, products where vendors.vend_id = products.vend_id order by vend_name, prod_name;

prod_nameprod_price 在一个表中,而列 vend_name 在另一个表中。from 子句列出了两个表,分别是 vendorsproducts。这两个表用 where 子句联结。

笛卡儿积(cartesian product)由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。

内部联结

目前为止所用的联结称为等值联结(equijoin)(也叫内部联结),使用 join 来表示:

select vend_name, prod_name, prod_price from vendors inner join products on vendors.vend_id = products.vend_id;

此语句中的 from 子句与上面的不同。两个表之间的关系是 from 子句的组成部分,以 INNER JOIN 指定。在使用这种语法时,联结条件用特定的 on 子句 而不是 where 子句给出。传递给 on 的实际条件与传递给 where 的相同。

SQL 规范首选 INNER JOIN 语法。此外,尽管使用 where 子句定义联结的确比较简单,但是使用明确的联结语法能够确保不会忘记联结条件,有时候这样做也能影响性能。不要联结不必要的表。联结的表越多,性能下降越厉害。

当两张表的数据量比较大,又需要连接查询时,应该使用 FROM table1 JOIN table2 ON xxx 的语法,避免使用 FROM table1,table2 WHERE xxx 的语法,因为后者会在内存中先生成一张数据量比较大的笛卡尔积表,增加了内存的开销。

联结多个表

SQL 对一条 SELECT 语句中可以联结的表的数目没有限制。

使用前面子查询的例子:

select cust_name from customers where cust_id in (
select cust_id from orders where order_num in (select order_num from orderitems where pod_id = 'TNT2'));

可以改造为:

select cust_name from customers, orders, orderitems
where customers.cust_id = orders.cust_id
and orderitems.order_num = orders.order_num
and pod_id = 'TNT2';

高级联结

表别名

使用表别名:

  • 缩短 SQL 语句
  • 允许在单条 select 语句中多次使用相同的表

使用表别名的主要原因之一是能在单条 select 语句中不止一次引用相同的表

select cust_name from customers as c, orders as o, orderitems as oi
where c.cust_id = o.cust_id
and oi.order_num = o.order_num
and pod_id = 'TNT2';

外部联结

select customers.cust_id, orders.order_num from customers inner join orders on customers.cust_id = orders.cust_id;

上面的语句很简单,就是检索所有客户及其订单。

那么如果想要检索所有客户,包括没有订单的客户,如下:

select customers.cust_id, orders.order_num from customers left outer join orders on customers.cust_id = orders.cust_id;

这条语句使用了关键字 OUTER JOIN 来指定联结的类型。外部联结还包括没有关联行的行。在使用 OUTER JOIN 语法时,必须使用 RIGHTLEFT 关键字指定包括其所有行的表RIGHT 指的是 OUTER JOIN 右边的表,而 LEFT 指的是 OUTER JOIN 左边的表)。

上面的例子使用 LEFT OUTER JOINfrom 子句的左边表(customers 表)中选择所有行。

组合查询

MySQL 允许执行多个查询(多条 select 语句),并将结果作为单个查询结果集返回。这些组合查询通常称为union)或复合查询compound query)。

select vend_id, prod_id, prod_price from products where prod_price >= 5
union
select vend_id, prod_id, prod_price from products where vend_id in (1001,1002);

转成多条 where 子句的写法:

select vend_id, prod_id, prod_price from products where prod_price >= 5 or vend_id in (1001,1002);

实际中应该试一下这两种方式,以确定对特定的查询哪一种性能更好。

union 规则

  • union 必须由两条或两条以上的 select 语句组成,语句之间用关键字 union 分隔。
  • union 中的每个查询必须包含相同的列、表达式或聚集函数(列的次序无所谓)。
  • 列数据类型必须兼容:类型不必完全相同,但必须是 DBMS 可以隐含地转换的类型(例如,不同的数值类型或不同的日期类型)。

包含或取消重复的行

union 从查询结果集中自动去除了重复的行。如果想返回所有匹配行,使用 union all 而不是 union

组合查询结果排序

union 组合查询时,只能使用一条 order by 子句,它必须出现在最后一条 select 语句之后。对于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条 order by 子句。

最后更新于