1 创建高级联结(别名、自联结、外联结)
1.1 使用表别名AS
使用别名的两个主要理由:
- 缩短SQL语句。
- 允许在一条SELECT语句中多次使用相同的表。
SELECT cust_name, cust_contact
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 prod_id = 'RGAN01';
别名不仅能用于WHERE子句,还可以用于SELECT列表、ORDER BY子句以及其他语句部分。
1.2 使用不同类型的联结
除了内联结INNER JOIN之外,还有三种其他联结方式:自联结(self-join)、自然联结(natural join)和外联结(outer join)。
1.2.1 自联结self-join
自联结指的是要检索的两个表实际上是同一个表。
假如要给与Jim Jones同一公司的所有顾客发送一份邮件。
方法一:子查询
SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = ( SELECT cust_name
FROM Customers
WHERE cust_contact = 'Jim Jones');
方法二:自联结查询
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
AND c2.cust_contact = 'Jim Jones';
一般来说,许多DBMS处理联结远比处理子查询快得多。
1.2.2 自然联结natural join
在对表进行联结时,至少有一列(被联结的列)不止出现在一个表中。标准的内联结返回所有数据,相同的列甚至多次出现。自然联结排除多次出现,使每一列只返回一次。
自然联结要求只能选择那些唯一的列,一般通过对一个表使用通配符(SELECT *),而对其他表的列使用明确的子集来完成。
SELECT C.*, O.order_num, O.order_date,
OI.prod_id, OI.quantity, OI.item_price
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 prod_id = 'RGAN01';
在这个例子中,通配符只对第一个表使用。所有其他列明确列出,所以没有重复的列被检索出来。
事实上,我们迄今为止建立的每个内联结都是自然联结,很可能永远都不会用到不是自然联结的内联结。
1.2.3 外联结outer join
许多联结将一个表中的行与另一个表中的行相关联,但有时候需要包含没有关联的那些行。
例如:
- 对每个顾客下的订单进行计数,包括哪些至今尚未下订单的顾客。
- 列出所有产品以及订购数量,包括没有人订购的产品。
- 计算平均销售规模,包括哪些至今尚未下订单的顾客。
在上述例子中,联结包含了那些在相关表中没有关联行的行,这种联结称为外联结(outer join)。
内联结检索所有顾客及其订单:
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语法时,必须使用RIGHT或LEFT关键字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT指出的是OUTER JOIN左边的表)。
上面的例子使用LEFT OUTER JOIN从FROM子句左边的表(Customers表)中选择所有行。为了从右边的表中选择所有行,需要使用RIGHT OUTER JOIN,如下例所示。
SELECT Customers.cust_id, Orders.order_num
FROM Customers RIGHT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
还存在另一种外联结,就是全外联结(full outer join),它检索两个表中的所有行并关联那些可以关联的行,全外联结包含了两个表中的不关联的行。(Access、MariaDB、MySQL和SQLite不支持FULL OUTER JOIN语法)
SELECT Customers.cust_id, Orders.order_num
FROM Customers FULL OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
1.3 使用带聚集函数的联结
检索所有顾客及每个顾客所下的订单数:
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
聚集函数也可以方便地与其他联结一起使用。
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
1.4 使用联结和联结条件
- 注意所使用的联结类型。一般我们使用内联结,但使用外联结也有效。
- 保证使用正确的联结条件,否则会返回不正确的数据。
- 应该总是提供联结条件,否则会得出笛卡尔积。
- 在一个联结中可以包含多个表,甚至可以对每个联结采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们前分别测试每个联结,这会使故障排除更为简单。
2 组合查询(UNION)
2.1 组合查询
多数SQL查询只包含从一个或多个表中返回数据的单条SELECT语句,但是SQL也允许执行多个查询(多条SELECT语句),并将结果作为一个查询结果集返回。这些组合查询通常称为并(union)或复合查询(compound query)。
使用组合查询的两个主要情况:
- 在一个查询中从不同的表返回结构数据。
- 对一个表执行多个查询,按一个查询返回数据。
2.2 创建组合查询
可用UNION操作符来组合数条SQL查询,利用UNION,可给出多条SELECT语句,将它们的结果组合成一个结果集。
2.2.1 使用UNION
给出每条SELECT语句,在各条语句之间放上关键字UNION。
例如,需要Illinois、Indiana和Michigan等美国几个州的所有顾客的报表,还想包括不管位于哪个州的所有的Fun4All。当然可以利用WHERE子句来完成此工作,不过这次我们使用UNION。
使用WHERE语句:
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
OR cust_name = 'Fun4All';
使用UNION语句:
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
在这个简单的例子中,使用UNION可能比使用WHERE子句更为复杂。但对于较复杂的过滤条件,或者从多个表(而不是一个表)中检索数据的情形,使用UNION可能会使处理更简单。
2.2.2 UNION规则
- UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔。如果组合四条SELECT语句,将要使用三个UNION关键字。
- UNION中的每个查询必须包含相同的列、表达式或聚集函数,各个列不需要以相同的次序列出。
- 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含转换的类型(例如,不同的数值类型或不同的日期类型)。
2.2.3 包含或取消重复的行
UNION从查询结果集中自动去除了重复的行,它的行为与一条SELECT语句中使用多个WHERE子句条件一样。使用UNION时,重复的行会自动取消。
如果想返回所有的匹配行,可使用UNION ALL而不是UNION。
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
2.2.4 对组合查询结果排序
在用UNION组合查询时,只能使用一条ORDER BY子句,它必须位于最后一条SELECT语句之后。
对于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY子句。
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All'
ORDER BY cust_name, cust_contact;
虽然ORDER BY子句似乎只有最后一条SELECT语句的组成部分,但实际上DBMS将用它来排序所有SELECT语句返回的所有结果。
如果您发现文中有不清楚或者有问题的地方,请在下方评论区留言,我会根据您的评论,更新文中相关内容,谢谢!