MySQL面试试题(五)

壬戌之秋,七月既望。眼看就要七月了,回首六月,好像离预期的目标还是太远。大规模仿真实验还在跑跑跑,参数还在调调调,糟心之余,唯一欣慰的是,总共完成了十篇博客,涵盖MySQL和机器学习。本次主要是总结MySQL中的联结组合查询

一、联结表

在阐述联结这个词之前我们先了解一些概念:
关系表
举个例子,这里有一个包含产品目录的数据库,

prod_id—产品编号
vend_id—供应商编号
prod_name—产品名称
prod_price—产品价格
prod_desc—产品描述
在这里插入图片描述
如果同一供应商生产多种物品,那么如何储存供应商消息?
一般的,我们会重新创建一个表格,来储存供应商的信息。因为可能会有多个产品对应同一供应商,如果放在同一表格的话,会造成数据重复。如果放在不同的表格,一旦供应商信息发生改变,只需要修改一次即可。
因此,我们为供应商单独建立了一个表格,
在这里插入图片描述
在关系型数据库中,各个表通过某一字段关联。Products表只储存产品信息,包括了供应商ID,vend_id表的主键又叫products的外键,它将products表和vendors关联起来。
外键:外键为某一个表的一个字段,同时又为另一个表的主键。
为什么要使用联结?
前面我们讲了,关系型数据库中多个表通过某一字段关联,这样更方便处理,更有效率。但问题是如果数据储存在多个表中,怎么样用单条SELECT语句把我们想要的数据检索出来呢?使用联结可以解决这个问题。联结是一种机制,用来在一条SELECT语句中关联表。

1.创建联结

创建一个联结,返回供应商名称以及提供的产品,

SELECT  vend_name,prod_name,prod_price
FROM    vendors,products
WHERE   vendors.vend_id = products.vend_id;

结果:
在这里插入图片描述
在联结两个表时,第一个表的每一行与第二个表的每一行配对。WHERE子句作为过滤条件,它只包含那些匹配给定条件的行。如果没有WHERE子句,第一个表格的每一行会与第二个表格的每一行配对。
在这里插入图片描述

2.内部联结

我们之前在WHERE子句中所使用的联结用等号连接,称为等值联结,它基于两个表格的相等测试。这种联结也叫内部联结

SELECT  vend_name,prod_name,prod_price
FROM    vendors INNER JOIN products
ON   vendors.vend_id = products.vend_id;

结果:
在这里插入图片描述

3.联结多个表

一条SELECT语句可以联结多个表,数目没有限制,创建表的规则相同。

SELECT  prod_name,vend_name,prod_name,quantity
FROM    products a,vendors b,orderitems c
WHERE   a.vend_id = b.vend_id AND a.prod_id = c.prod_id AND order_num = 20005;

结果:
在这里插入图片描述
在第十四章中,我们要列出订购物品TNT2的所有客户,之前使用的是子查询,由内到外依次展开,

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 = 'TNT2'));

这里使用联结,其中WHERE子句中包含三个条件,前两个关联联结中的表,后一个是过滤产品ID为TNT2的数据。

SELECT   cust_name,cust_contact
FROM     orderitems a,orders b,customers c
WHERE    a.order_num = b.order_num  AND b.cust_id = c.cust_id AND a.prod_id ='TNT2';

结果:
在这里插入图片描述

二、创建高级联结

除了内联结以外,还有其他的联结类型。

1.使用表别名

为了缩短SQL语句,允许在单条SELECT语句中多次使用相同的表,我们使用别名。别名除了用于列名和计算字段之外,还可以用于表。其实前面我们已经使用过。

SELECT   cust_name,cust_contact
    FROM     orderitems a,orders b,customers c
    WHERE    a.order_num = b.order_num  AND b.cust_id = c.cust_id AND a.prod_id ='TNT2';

我们给三个表格分别取名a,b,c,在where子句里,我们就可以使用这些别名,而不用写全称。

2.使用不同类型的联结

前面我们在select语句中使用过内部联结(等值联结),这里再介绍三种联结方式
1)自联结
问题背景:假如你发现某物品(其ID为DTNTR)存在问题,因此想知道生产该物品的供应商生产的其他物品是否也有问题。
第一种方式:使用子查询

SELECT   prod_id,prod_name
    FROM     products
    WHERE    vend_id  IN(SELECT vend_id 
                         FROM   products
                         WHERE  prod_id = 'DTNTR');

结果:
在这里插入图片描述
第二种方式:使用自联结

SELECT   b.prod_id ,b.prod_name
FROM     products a,products b
WHERE    a.vend_id = b.vend_id AND a.prod_id = 'DTNTR';

结果相同,不再展示。使用的两个表是同一个表,实际上是同一个表使用了两次。

2)自然联结
自然联结其实和内部联结一样,只是不会出现相同的重复列。

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 = 'FB';

结果:
在这里插入图片描述
字体太小,但我们可以发现,没有重复的列出现。
而如果你直接使用内部联结,

SELECT  *
FROM    vendors INNER JOIN products
ON   vendors.vend_id = products.vend_id;

结果:
在这里插入图片描述
可以发现,内部联结把将两个表中存在联结关系的字段符合联结关系的那些记录形成记录集的联结。这里就是把 id 相等的部分罗列出来。
3)外部联结
外部联结包含了在联结表中没有关联行的行,比如

  • 对每个客户下了多少订单进行计数,包括哪些没有下订单的客户;
  • 列出所有产品以及订购数量,包括没有被订购的产品;

外部联结包括左联结(left outer join on)和右联结(right outer join on)。
我们把内部联结和外部联结对比一下这里是查询顾客对应的订单:
内部联结:
SELECT a.cust_id,order_num
FROM customers a INNER JOIN orders b
ON a.cust_id = b.cust_id;
结果:
在这里插入图片描述
外部联结:

SELECT  a.cust_id,order_num
FROM    customers a LEFT OUTER JOIN orders b
ON      a.cust_id = b.cust_id;

结果:
在这里插入图片描述
从这两个结果,我们可以发现,内部联结表把两个表中符合id相等的行取出来,而外部联结中,左边的行一定会列出,右边的表中如果没有匹配的行,则列值为Null。特别需要注意的是如果右表有多行和左表匹配,那么左表相同的行会出现多次。

3.使用带聚集函数的联结

之前的聚集函数只是在一个表中进行汇集数据。如果在多个表中汇集数据,需要使用联结。
比如,要检索所有客户以及每个客户下的订单数,

SELECT  cust_name,c.cust_id,COUNT(*) AS order_num
FROM    orders o INNER JOIN customers c
ON   o.cust_id = c.cust_id
GROUP BY  o.cust_id;

结果:
在这里插入图片描述
如果要包含那些没有下订单的顾客,

SELECT  cust_name,c.cust_id,COUNT(order_num) AS order_num
FROM    orders o RIGHT OUTER JOIN  customers c
ON   o.cust_id = c.cust_id
GROUP BY  c.cust_id;

结果:
在这里插入图片描述
注意:

  • 不要忘记提供联结条件,否则会得出笛卡尔积;
  • 一个联结中可以包含多个表,每个联结可以包含不同的联结类型。在一起测试这些联结之前,应该分别测试每个联结,利于排除故障。

三、组合查询

之前我们所做的大部分查询都是包含从一个或多个表中返回查询结果的单条SELECT语句。MySQL允许多个查询(多条SELECT语句),并将结果作为单个查询结果返回。这些组合查询也被称为并-union或复合查询。
适用情况:

  • 在单个查询中从不同的表返回类似结构的数据;
  • 对单个表执行多个查询,按单个查询返回数据。
1.创建组合查询

UNION操作符来组合多条SELECT查询,并将结果组合成单个结果集。
在使用UNION时,只需要在每条select语句之间加上关键字UNION即可。
这里,我们要将两个SELECT语句进行组合,
第一个是检索价格不高于5的物品,

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5;

结果:
在这里插入图片描述
第二个是找出供应商1001和1002生产的所有物品,

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    vend_id  IN (1001,1002);

结果:
在这里插入图片描述
我们将这两条语句进行组合,

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 + OR 语句

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5 OR vend_id  IN (1001,1002);
2.使用UNION

有三条基本规则:

  • UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔
  • UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次序列出)
  • 列的数据类型必须兼容:类型不必完全相同

去重

UNION从查询结果集中自动去除了重复的行,如果想返回所有匹配的行,可使用UNION ALL

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5
UNION ALL
SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    vend_id  IN (1001,1002);

结果:
在这里插入图片描述
这里就包括了重复的行。一般地,UNION 几乎完成与多个 where 子句相同的工作。UNION ALLUNION 的一种形式,它完成WHERE子句完成不了的工作。如果确实需要每个条件的匹配行全部出现,则必须使用 UNION ALL而不是 WHERE

组合查询结果排序

单条select语句中的排序用 ORDER BY 排序。在用 UNION组合查询时,只能使用一条 ORDER BY ,它必须出现在最后一条 SELECT 语句之后。对于结果集,不允许用一种方式排序一部分,而又用另一种方式排序另一部分,不允许使用多条ORDER BY 子句。

对之前的组合结果使用ORDER BY 子句

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5
UNION ALL
SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    vend_id  IN (1001,1002)
ORDER BY vend_id,prod_id;

结果:
在这里插入图片描述

四、经典面试题

问题描述如下:
为管理岗位业务培训信息,建立三个表格:

  • Stu(Sid,SN,SD,SA)表:学生表
    Sid 表示学号
    SN 表示学员姓名
    SD 表示所属单位
    SA 表示学员年龄

  • Cou(Cid,CN)表:课程表
    Cid 课程编号
    CN 课程名称

  • Sco(Sid,Cid,G)表:分数表
    Sid 表示学号
    Cid 课程编号
    G 学习成绩

具体问题参考SQL脚本

################################################################################################################

-- 创建学生表

CREATE  TABLE  Stu(
   Sid  INT NOT NULL  PRIMARY  KEY,   
   SN   VARCHAR(20),
   SD   VARCHAR(50),
   SA   INT
 )ENGINE = INNODB;
-- 创建课程表
CREATE  TABLE  Cou(
   Cid   VARCHAR(10)  NOT  NULL  PRIMARY  KEY,
   CN    VARCHAR(20)
)ENGINE = INNODB;
-- 创建成绩表
CREATE   TABLE Sco(
   Sid   INT  NOT NULL,
   Cid   VARCHAR(10)  NOT NULL,
   G     DECIMAL(5,2),
   CONSTRAINT  fk_Sid  FOREIGN KEY  (Sid)  REFERENCES  Stu(Sid),
   CONSTRAINT  fk_Cid  FOREIGN KEY  (Cid)  REFERENCES  Cou(Cid)
)ENGINE = INNODB;
#################################################################################################################

-- 填充数据

INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019001,'托马斯李','运营',26);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019002,'米高扬','管理',30);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019003,'蝙蝠侠','安防',22);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019004,'李嘉诚','投资',45);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019005,'雷军','开发',34);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019006,'周小川','管理',56);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019007,'陆奇','运营',36);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019008,'普京','安防',67);

INSERT  INTO  Cou(Cid,CN)   VALUES('C1','税收基础');
INSERT  INTO  Cou(Cid,CN)   VALUES('C2','金融工程');
INSERT  INTO  Cou(Cid,CN)   VALUES('C3','会计');
INSERT  INTO  Cou(Cid,CN)   VALUES('C4','统计学习方法');
INSERT  INTO  Cou(Cid,CN)   VALUES('C5','大数据');
INSERT  INTO  Cou(Cid,CN)   VALUES('C6','机器学习算法');

INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019001,'C2',80);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019002,'C2',78);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019003,'C1',89);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019003,'C5',60);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019004,'C4',90);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C1',87);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C2',75);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C3',80);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C4',90);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C5',86);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C6',88);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019006,'C1',99);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019006,'C2',61);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C1',62);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C2',78);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C3',77);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C4',69);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C5',98);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C6',88);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019008,'C1',78);
#################################################################################################################

-- 一、使用标准SQL嵌套语言查询选修课程名称为“税收基础”的学员学号与姓名

SELECT    Sid,SN
FROM      Stu 
WHERE     Sid   IN  (SELECT  Sid  
                     FROM    Sco
                     WHERE   Cid  IN  (SELECT  Cid  
                                       FROM    Cou 
                                       WHERE   CN  =  '税收基础'));
-- 二、使用标准SQL嵌套语言查询选修课程编号为“C2”的学员姓名和所属单位

SELECT   SN,SD
FROM     Stu 
WHERE    Sid   IN  (SELECT  Sid
                    FROM    Sco
                    WHERE   Cid = 'C2');
-- 三、使用标准SQL嵌套语言查询选修课程编号为“C5”的学员姓名和所属单位

SELECT  SN,SD
FROM    Stu p1 INNER JOIN  Sco  p2
ON      p1.Sid  =  p2.Sid  AND  p2.Cid  =  'C5';
-- 四、使用标准SQ嵌套语言查询选修全部课程的学员姓名和所属单位

SELECT  SN,SD
FROM    Stu a ,Sco b
WHERE   a.Sid = b.Sid
GROUP BY  b.Sid
HAVING  COUNT(Cid) =  (SELECT COUNT(Cid)
                       FROM   Cou); 
-- 五、查询选修课程的学员人数

SELECT   CN,COUNT(Sid)
FROM     Cou s1 INNER JOIN Sco s2
ON       s1.Cid  =  s2.Cid
GROUP BY s2.Cid;
-- 六、查询选修课程超过5门的学员学号和所属单位

SELECT   s2.Sid,SD
FROM     Sco s1 INNER JOIN  Stu  s2
ON       s1.Sid  =  s2.Sid  
GROUP BY  s1.Sid
HAVING    COUNT(Cid) > 5;

参考资料:
1.《MySQL必知必会》 Ben Forta;
2.sql面试题(学生表_课程表_成绩表_教师表):https://www.cnblogs.com/qixuejia/p/3637735.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章