内连接与外连接-及其典型案例

Posted EbowTang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内连接与外连接-及其典型案例相关的知识,希望对你有一定的参考价值。

在数据库系统中,join用于比较和组合(字面意思是连接)并从数据库中的两个或多个表返回特定数据行。内连接从表中查找并返回匹配数据,而外连接从表中查找并返回匹配数据和一些不同的数据。

内连接

内连接侧重于两个表之间的共性。使用内部联接时,要比较的两个(或多个)表之间,必须有一些匹配数据,即链接的条件。内部联接在表中搜索匹配或重叠的数据。找到后,内连接将信息合并并返回到一个新表中。

SELECT 字段列表
FROM A表 INNER JOIN B表
ON 关联条件
WHERE 等其他子句;

内连接例子

让我们考虑两个表的常见场景:产品价格和数量。两个表中的公共信息是产品名称,因此这是连接表的逻辑列。有一些产品在两个表中是通用的;其他的对于其中一个表是唯一的,并且在另一个表中没有匹配项。

下面是通过产品名称作为连接条件,将符合条件的信息返回

外连接

外连接返回一组记录(或行),其中包括内连接将返回的内容,但也包括在其他表中找不到对应匹配项的其他行。
外连接分为三种类型:

  • 左外连接(或左连接)
  • 右外连接(或右连接)
  • 完全外连接(或完全连接)

这些外部联接中的每一个都指的是正在比较、组合和返回的数据部分。有时在此过程中会产生null,因为某些数据是共享的,而其他数据则不是。

左连接

左外连接将返回表 1中的所有数据和所有共享数据,但仅返回表 2 中的相应数据,即左连接。

SELECT 字段列表
FROM A表 LEFT JOIN B表
ON 关联条件
WHERE 等其他子句;

左连接例子

在我们的示例数据库中,“左边”(prices表)有两种产品——oranges和 tomatoes ,在“右边”(quantities表)上没有相应的条目。在左连接中,这些行包含在结果集中,数量列中为 NULL。结果中的其他行与内部联接相同。

右连接

右外连接返回表 2 的数据和所有共享数据,但仅返回表 1 中的相应数据,即右连接。

 
SELECT 字段列表
FROM A表 RIGHT JOIN B表
ON 关联条件
WHERE 等其他子句;

右连接例子

与左连接示例类似,右外连接的输出包括内连接的所有行和来自“右”(quantities表)的两行 - broccoli 和squash - 在左侧没有匹配的条目。

全连接

流行的 mysql 数据库管理系统不支持的完全外连接或完全连接,无论是否存在共享信息,都会合并并返回来自两个或多个表的所有数据。将完全连接视为简单地复制所有指定信息,但在一个表中,而不是在多个表中。在缺少匹配数据的情况下,将产生空值。

SELECT 字段列表
FROM A表 FULL JOIN B表
ON 关联条件
WHERE 等其他子句;

这些只是基础知识,但很多事情都可以通过连接来完成。甚至还有可以排除其他连接的连接!

多表连接问题

用户信息表:user_profile

iddevice_idgenderageuniversitygpaactive_days_within_30question_cntanswer_cnt
12138male21北京大学3.47212
23214maleNULL复旦大学415525
36543female20北京大学3.212330
42315female23浙江大学3.6512
55432male25山东大学3.8201570
62131male28山东大学3.315713
74321male28复旦大学3.69652

第一行表示:id为1的用户的常用信息为使用的设备id为2138,性别为男,年龄21岁,北京大学,gpa为3.4,在过去的30天里面活跃了7天,发帖数量为2,回答数量为12

最后一行表示:id为7的用户的常用信息为使用的设备id为432,性别为男,年龄28岁,复旦大学,gpa为3.6,在过去的30天里面活跃了9天,发帖数量为6,回答数量为52

题库练习明细表:question_practice_detail

iddevice_idquestion_idresult
12138111wrong
23214112wrong
33214113wrong
4

6534

111right
52315115right
62315116right
72315117wrong
85432117wrong
95432112wrong
102131113right
115432113wrong
122315115right
132315116right
142315117wrong
155432117wrong
165432112wrong
172131113right
185432113wrong
192315117wrong
205432117wrong
215432112wrong
222131113right
235432113wrong


第一行表示:id为1的用户的常用信息为使用的设备id为2138,在question_id为111的题目上,回答错误

......

最后一行表示:id为23的用户的常用信息为使用的设备id为5432,在question_id为113的题目上,回答错误

表:question_detail

idquestion_iddifficult_level
1111hard
2112medium
3113easy
4115easy
5116medium
6117easy

第一行表示: 题目id为111的难度为hard

....

第一行表示: 题目id为117的难度为easy

SQL23 统计每个学校各难度的用户平均刷题数

请你写一个SQL查询,计算不同学校、不同难度的用户平均答题量,根据示例,你的查询应返回以下结果(结果在小数点位数保留4位,4位之后四舍五入):

universitydifficult_levelavg_answer_cnt
北京大学hard1.0000
复旦大学easy1.0000
复旦大学medium1.0000
山东大学easy4.5000
山东大学medium3.0000
浙江大学easy5.0000
浙江大学medium2.0000

解释:

第一行:北京大学有设备id为2138,6543这2个用户,这2个用户在question_practice_detail表下都只有一条答题记录,且答题题目是111,从question_detail可以知道这个题目是hard,故 北京大学的用户答题为hard的题目平均答题为2/2=1.0000

第二行,第三行:复旦大学有设备id为3214,4321这2个用户,但是在question_practice_detail表只有1个用户(device_id=3214有答题,device_id=4321没有答题,不计入后续计算)有2条答题记录,且答题题目是112,113各1个,从question_detail可以知道题目难度分别是medium和easy,故 复旦大学的用户答题为easy, medium的题目平均答题量都为1(easy=1或medium=1) /1 (device_id=3214)=1.0000

第四行,第五行:山东大学有设备id为5432和2131这2个用户,这2个用户总共在question_practice_detail表下有12条答题记录,且答题题目是112,113,117,且数目分别为3,6,3,从question_detail可以知道题目难度分别为medium,easy,easy,所以,easy共有9个,故easy的题目平均答题量= 9(easy=9)/2 (device_id=3214 or device_id=5432) =4.5000,medium共有3个,medium的答题只有device_id=5432的用户,故medium的题目平均答题量= 3(medium=9)/1 ( device_id=5432) =3.0000

.....

SQL24 统计每个用户的平均刷题数

题目:运营想要查看参加了答题的山东大学的用户在不同难度下的平均答题题目数,请取出相应数据

请你写一个SQL查询,计算山东、不同难度的用户平均答题量,根据示例,你的查询应返回以下结果(结果在小数点位数保留4位,4位之后四舍五入):

universitydifficult_levelavg_answer_cnt
山东大学easy4.5000
山东大学medium3.0000


山东大学有设备id为5432和2131这2个用户,这2个用户总共在question_practice_detail表下有12条答题记录,且答题题目是112,113,117,且数目分别为3,6,3,从question_detail可以知道题目难度分别为medium,easy,easy,所以,easy共有9个,故easy的题目平均答题量= 9(easy=9)/2 (device_id=3214 or device_id=5432) =4.5000,medium共有3个,medium的答题只有device_id=5432的用户,故medium的题目平均答题量= 3(medium=9)/1 ( device_id=5432) =3.0000

--题目:运营想要计算一些参加了答题的不同学校、不同难度的用户平均答题量,请你写SQL取出相应数据
-----左连接
select UNIVERSITY,DIFFICULT_LEVEL,ROUND(COUNT(QPD.QUESTION_ID) / COUNT(distinct QPD.DEVICE_ID), 4) as AVG_ANSWER_CNT
from QUESTION_PRACTICE_DETAIL QPD left join USER_PROFILE UP on UP.DEVICE_ID=QPD.DEVICE_ID
left join question_detail qd ON qd.question_id=qpd.question_id
group by UNIVERSITY, DIFFICULT_LEVEL;
----另一种写法,内连接
select UNIVERSITY , DIFFICULT_LEVEL , ROUND(COUNT(QPD.QUESTION_ID)/COUNT(distinct QPD.DEVICE_ID),4) AVG_ANSWER_CNT  ----根据分组计算的结果结算用户平均答题量
from USER_PROFILE UP 
inner join QUESTION_PRACTICE_DETAIL QPD on UP.DEVICE_ID = QPD.DEVICE_ID
inner join QUESTION_DETAIL QD on QPD.QUESTION_ID = QD.QUESTION_ID --三个表中查询数据
group by UNIVERSITY , DIFFICULT_LEVEL;   --计算不同学校、不同难度
-----等值连接
select UNIVERSITY , DIFFICULT_LEVEL , round(COUNT(QPD.QUESTION_ID)/COUNT(distinct QPD.DEVICE_ID),4) AVG_ANSWER_CNT  ----根据分组计算的结果结算用户平均答题量
from USER_PROFILE UP , QUESTION_PRACTICE_DETAIL QPD , QUESTION_DETAIL QD  --三个表中查询数据
where UP.DEVICE_ID = QPD.DEVICE_ID and QPD.QUESTION_ID = QD.QUESTION_ID  --建立内连接
group by UNIVERSITY , DIFFICULT_LEVEL;   --计算不同学校、不同难度
--测试
SELECT *
from USER_PROFILE UP , QUESTION_PRACTICE_DETAIL QPD, QUESTION_DETAIL QD
WHERE up.device_id = qpd.device_id AND qpd.question_id = qd.question_id;

----题目:运营想要查看参加了答题的山东大学的用户在不同难度下的平均答题题目数,请取出相应数据
select  UNIVERSITY , DIFFICULT_LEVEL , COUNT(QPD.QUESTION_ID)/COUNT(distinct QPD.DEVICE_ID) AVG_ANSWER_CNT
from USER_PROFILE UP , QUESTION_PRACTICE_DETAIL QPD , QUESTION_DETAIL QD
where UP.DEVICE_ID = QPD.DEVICE_ID and QPD.QUESTION_ID = QD.QUESTION_ID
group by UNIVERSITY,DIFFICULT_LEVEL
having UNIVERSITY='山东大学';

--------------创建实验表
drop table  user_profile;
DROP TABLE  question_practice_detail;
drop table  question_detail;
CREATE TABLE user_profile (
id int NOT NULL,
device_id int NOT NULL,
gender varchar(14) NOT NULL,
age int ,
university varchar(32) NOT NULL,
gpa float,
active_days_within_30 int ,
question_cnt int ,
answer_cnt int 
);
CREATE TABLE question_practice_detail (
id int NOT NULL,
device_id INT NOT NULL,
question_id int NOT NULL,
result varchar(32) NOT NULL
);
CREATE TABLE question_detail (
ID INT NOT NULL,
question_id int NOT NULL,
difficult_level varchar(32) NOT NULL
);

SELECT * FROM question_practice_detail; --题库练习明细表
SELECT * FROM question_detail;  --
SELECT * FROM user_profile;     --用户信息表

INSERT INTO user_profile VALUES(1,2138,'male',21,'北京大学',3.4,7,2,12);
INSERT INTO user_profile VALUES(2,3214,'male',null,'复旦大学',4.0,15,5,25);
INSERT INTO user_profile VALUES(3,6543,'female',20,'北京大学',3.2,12,3,30);
INSERT INTO user_profile VALUES(4,2315,'female',23,'浙江大学',3.6,5,1,2);
INSERT INTO user_profile VALUES(5,5432,'male',25,'山东大学',3.8,20,15,70);
INSERT INTO user_profile VALUES(6,2131,'male',28,'山东大学',3.3,15,7,13);
INSERT INTO user_profile VALUES(7,4321,'male',28,'复旦大学',3.6,9,6,52);
INSERT INTO question_practice_detail VALUES(1,2138,111,'wrong');
INSERT INTO question_practice_detail VALUES(2,3214,112,'wrong');
INSERT INTO question_practice_detail VALUES(3,3214,113,'wrong');
INSERT INTO question_practice_detail VALUES(4,6543,111,'right');
INSERT INTO question_practice_detail VALUES(5,2315,115,'right');
INSERT INTO question_practice_detail VALUES(6,2315,116,'right');
INSERT INTO question_practice_detail VALUES(7,2315,117,'wrong');
INSERT INTO question_practice_detail VALUES(8,5432,117,'wrong');
INSERT INTO question_practice_detail VALUES(9,5432,112,'wrong');
INSERT INTO question_practice_detail VALUES(10,2131,113,'right');
INSERT INTO question_practice_detail VALUES(11,5432,113,'wrong');
INSERT INTO question_practice_detail VALUES(12,2315,115,'right');
INSERT INTO question_practice_detail VALUES(13,2315,116,'right');
INSERT INTO question_practice_detail VALUES(14,2315,117,'wrong');
INSERT INTO question_practice_detail VALUES(15,5432,117,'wrong');
INSERT INTO question_practice_detail VALUES(16,5432,112,'wrong');
INSERT INTO question_practice_detail VALUES(17,2131,113,'right');
INSERT INTO question_practice_detail VALUES(18,5432,113,'wrong');
INSERT INTO question_practice_detail VALUES(19,2315,117,'wrong');
INSERT INTO question_practice_detail VALUES(20,5432,117,'wrong');
INSERT INTO question_practice_detail VALUES(21,5432,112,'wrong');
INSERT INTO question_practice_detail VALUES(22,2131,113,'right');
INSERT INTO question_practice_detail VALUES(23,5432,113,'wrong');
INSERT INTO question_detail VALUES(1,111,'hard');
INSERT INTO question_detail VALUES(2,112,'medium');
INSERT INTO question_detail VALUES(3,113,'easy');
INSERT INTO question_detail VALUES(4,115,'easy');
INSERT INTO question_detail VALUES(5,116,'medium');
INSERT INTO question_detail VALUES(6,117,'easy');

对查询结果进行组合

SQL25 查找山东大学或者性别为男生的信息

题目:现在运营想要分别查看学校为山东大学或者性别为男性的用户的device_id、gender、age和gpa数据,请取出相应结果,结果不去重。

示例:user_profile

iddevice_idgenderageuniversitygpaactive_days_within_30question_cntanswer_cnt
12138male21北京大学3.47212
23214male复旦大学415525
36543female20北京大学3.212330
42315female23浙江大学3.6512
55432male25山东大学3.8201570
62131male28山东大学3.315713
74321male26复旦大学3.69652

根据示例,你的查询应返回以下结果(注意输出的顺序,先输出学校为山东大学再输出性别为男生的信息):

device_idgenderagegpa
5432male253.8
2131male283.3
2138male213.4
3214maleNone4
5432male253.8
2131male283.3
4321male283.6
select device_id , gender , age , gpa from user_profile where university = '山东大学' 
UNION  --去重
select device_id , gender , age , gpa from user_profile where gender = 'male'


select device_id , gender , age , gpa from user_profile where university = '山东大学' 
UNION ALL --不去重,这是正确答案
select device_id , gender , age , gpa from user_profile where gender = 'male'

以上是关于内连接与外连接-及其典型案例的主要内容,如果未能解决你的问题,请参考以下文章

SQL内连接与外连接的区别

内连接与外连接(全网最详细)

MySQL 内连接与外连接

内连接与外连接

SQL内连接与外连接用法与区别

2020.03.14 内连接与外连接的区别