(七)数据库 | 子查询与联结查询

发布于 2021-09-08 11:26

TheWordsofLove

数据检索


上一期我们介绍了分组查询(GROUP BY HAVING)和组合查询(UNION [ALL]),本期介绍数据检索中最后两个非常重要的查询,子查询与联结查询。子查询(SELECT(SELECT))和联结查询(JOIN ON)是数据检索中最强大的两个功能。

1、子查询





上期的第三个实例中(查询05年9月份订单总价大于等于50美元的订单号及订单总价格)已经使用过子查询进行数据过滤,这里利用子查询检索了05年9月份的所有订单编号。从这个例子中我们可以看到,子查询(subquery)就是嵌套在其它查询语句中的查询!
通常,我们搭配WHERE子句和subquery以过滤数据或者利用子查询和函数、算术运算操作符创建计算字段以返回所需的非自然列(即表中实际不存在的列)。

1、用子查询过滤数据

数据库通常不会存储冗余数据,比如顾客表中仅有顾客信息,订单信息则分别存储在订单列表和订单详情两个表中,订单列表中仅存储订单号(主键)、顾客ID(外键)和订单时间,订单详情表中用来存储每个订单中的所有物品,它以订单号与商品号主属性组为主键,这样就避免了日期和顾客ID的冗余存储。

查询订购产品TNT2的顾客信息:

SELECT cust_name, cust_contact, concat(cust_city, '(', cust_state, ',', cust_country, ')')

FROM customers

WHERE cust_id IN (SELECT cust_id 

                                FROM orders

                                WHERE order_num IN (SELECT order_num

                                                                        FROM orderitems

                                                                        WHERE prod_id = 'TNT2'));

通过产品ID查询到订购该产品订单的订单号,再在订单列表中通过订单号查询到顾客ID,再在顾客表中利用顾客ID查询所需要的顾客信息。

注意:为了便于调试,强烈建议在使用子查询时进行适当地格式化处理(缩进、括号)等!

2、用子查询创建计算字段

查询所有顾客的订单总数:

SELECT cust_id, cust_name, cust_contact,

(SELECT COUNT(*) FROM orders WHERE customers.cust_id = orders.cust_id) AS order_quantity 

FROM customers # 理解:查询语句的执行方式是“逐行查询“

ORDER BY cust_id;

SELECT c.cust_id, cust_name, cust_contact, 

COUNT(order_num) AS order_quantity

FROM customers AS c LEFT OUTER JOIN orders AS o

ON c.cust_id = o.cust_id # 使用外部联结建立联结表后再统计客户的订单数量

GROUP BY c.cust_id

ORDER BY c.cust_id;

小贴士


1、任何检索任务都不止一种实现方式,尝试用更多的方式!

2、子查询是一种非常有技巧性的数据处理方式,建立子查询时应”层层渐进“,从内而外地逐渐建立嵌套!

3、在列名(表名)出现二义性时,必须使用完全限定列名(表名)消除二义性。

2、联结查询





01

INNER JOIN ON



 内部联结

在上期第三个任务的第二个实现方法中就已经使用过(内部)联结,它将订单表和订单详情表联结成一张虚表,称之为”联结表“,联结表并不存储在数据库中,我们实施的查询是建立在这张联结表基础之上的!

联结实则是一种机制,它用来关联一条SELECT查询语句中的两个或多个表。内部联结(INNER JOIN ON)是使用最多的连接方式!创建内部联结十分简单,只需要像上面那样指定需要进行联结的表(如orders和orderitems)和用来建立联结的纽带字段(如order_num)即可!

1、查询所有产品的产品名称、价格及对应的供应商名称:由于产品信息和供应商信息分别存储在产品表和供应商表中,所以需要建立联结查询,两表通过键vend_id进行内部联结>>

SELECT vend_name, prod_name, prod_price

FROM vendors, products # 指定需要联结的表

WHERE vendors.vend_id = products.vend_id # 指定联结字段

ORDER BY vend_name, prod_name;

SELECT vend_name, prod_name, prod_price

FROM vendors INNER JOIN products # 指定联结方式(内部)和需要联结的表

ON vendors.vend_id = products.vend_id # 指定连接字段

ORDER BY vend_name, prod_name;

2、类似地,可以建立多表联结:查询编号为20005的订单中的所有产品的产品名称、价格、供应商名称与购买数量,需要联结表订单详情表、产品表和供应商表,其中订单详情表和产品表通过prod_id联结,产品表和供应商表通过vend_id联结>>

SELECT prod_name, prod_price, vend_name, quantity
FROM products, vendors, orderitems
WHERE products.vend_id = vendors.vend_id AND  
              products.prod_id = orderitems.prod_id AND 
              order_num = '20005'
ORDER BY prod_name, vend_name;

注意


1、在建立联结表时,不要忘了用WHERE子句建立联结条件,没有联结条件的表关系返回的联结结果称为”笛卡尔积“,这种无条件联结称为叉联结(cross join),笛卡尔积联结表的数据量为第一个表的数据量×第二个表的数据量,数据量非常恐怖!

2、内部联结(INNER JOIN)又称为等值联结,除了可以用相等操作符(=)建立之外,还可以使用FROM  table1 INNER JOIN table2 ON table1.column = table2.column来建立内部联结。


02

OUTER JOIN ON



 高级联结

1、自联结,即利用表别名创建表自身之间的联结,它是内部联结的一个特例,只是用来建立联结表的两个表相同,请看自联结示例:

SELECT COUNT(*) AS prod_quantity

FROM products AS p1, products AS p2 # 产品表通过vend_id进行自联结

WHERE p1.vend_id = p2.vend_id; # 查询结果为66=3^2+2^2+7^2+2^2

自联结在数据检索过程中也是有用的,如进行合格检验时发现ID为DTNTR的产品不合格,现需要对提供该产品的供应商所提供的全部产品进行抽检,则需要查询该供应商的所有产品信息>>

SELECT p1.prod_id, p1.prod_name

FROM products AS p1, products AS p2

WHERE p1.vend_id = p2.vend_id

AND p2.prod_id = 'DTNTR'

ORDER BY p1.prod_id;

SELECT prod_id, prod_name

FROM products # 使用子查询也是可以的

WHERE vend_id IN (SELECT vend_id 

                                  FROM products

                                  WHERE prod_id = 'DTNTR')

ORDER BY prod_id;

2、外部联结(OUTER JOIN ON)

外部联结是内部联结的扩展,外部联结表中还包含不存在匹配值的数据行(外部联结的声明方式和内部联结完全一样,见示例)!如客户10002没有下过订单,如果使用内部联结联结客户表和订单表,则联结表中不会出现客户10002,如果使用外部联结,则联结表中会给出客户10002,只是该客户不存在订单信息而已>>

SELECT c.cust_id, cust_name, cust_email, order_num, order_date

FROM customers AS c INNER JOIN orders 

ON c.cust_id = orders.cust_id 

ORDER BY c.cust_id; 

# 内部联结,由于orders表中没有顾客10002,联结表中也没有

SELECT c.cust_id, cust_name, cust_email, order_num, order_date

FROM customers AS c LEFT OUTER JOIN orders

ON c.cust_id = orders.cust_id

ORDER BY c.cust_id;

 # 外部联结,由于customers表中含有顾客10002,联结表中就有

检索实例,查询所有顾客的订单总数:

SELECT c.cust_id, cust_name, cust_contact, 

COUNT(order_num) AS order_quantity

FROM customers AS c LEFT OUTER JOIN orders AS o

ON c.cust_id = o.cust_id # 使用外部联结建立联结表后再统计客户的订单数量

GROUP BY c.cust_id

ORDER BY c.cust_id;

注意


1、在使用联结时,必须给出联结条件,否则会返回关系表之间的笛卡尔积!

2、相较于外部联结,内部联结使用更为频繁,两种联结都应该给出正确的联结条件,否则错误的联结表会导致错误的结果!

本期精彩

子查询(嵌套SELECT,过滤数据,创建计算字段)

联结查询(内部联结INNER JOIN ON,自联结)

高级联结(外部联结LEFT[RIGHT] OUTER JOIN ON)

统计美学
专注于数据分析和统计应用的学习平台
28篇原创内容

本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。

相关素材