一、 表的加法:union语句

1、union 语句

使用union语句进行course表跟course1相加 ,两个查询语句联结在一起,
文氏图:左边course 右边course1
两个表相加重复的数据只会留一个:例如语文课程0001




postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表


union SQL语句的使用


-- union语句的使用
Select 课程号,课程名称
From course
Union
Select 课程号,课程名称
From course1


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_02


2、union all 语句

union all 将所有的列进行相联结,不会删除重复的查询列


-- union all SQL语句的使用
Select 课程号,课程名称
From course
Union all
Select 课程号,课程名称
From course1


postgres 两个sql加减 两个sql结果相加_sql 列求和_03


二、 表的联结

什么是联结?

联结:表和表是通过列产生对应关系的,联结是将通过表和表之间的关系,将两个表合并在一起的操作。

1、 各表之间的关系

各个表通过一些相同的列进行联结的
例如下图每个学号列相同的学号行进行联结


postgres 两个sql加减 两个sql结果相加_sql 相加_04


4张表的联结关系图

学生表跟成绩表通过学号联结
成绩表跟课程表通过课程号进行联结
课程表跟教师表通过教师号进行联结


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_05


postgres 两个sql加减 两个sql结果相加_sql 两条数据 空值合并_06


2、 常用的联结联结分类

1) 交叉联结:cross join

也叫笛卡尔积
英文名称cross join

案例:

表一三行数据 跟 表二两行数据进行交叉联结
表一 cross join 表二 的数据表 为3行*2行=6行数据
类似扑克牌 13张牌=(A.2.3.4.5.6.7.8.9.10.J.Q.K)
花色=(♠.♥.♦.♣)
13张牌X花色=13*4=(52张扑克牌)

平常用的比较少

2)内联结:inner join

查找出同时存在于两张表中的数据:英文名 inner join

案例

学生表(student)跟成绩表(score)通过学号产生了匹配,在表中使用相同的颜色表示都有的学号。两个表里都有的学号是0001,学号0002只存在于学生表里,学号0005只存在于成绩表中,图片中用文氏图画出了内联结之间的关系,左边的圆圈显示学生表里的数据,右边的圆圈显示成绩表里的数据,两个圆圈重合的地方就是同时存在于两个表里的数据,也就是学号0001。


postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表_07


拿学生表跟成绩表进行内联结,两个表通过学生的学号进行内联结,下面可以看出内联结在两张表上是如何运行的

第一步:

两个表通过学号进行内联结,会将两个表中都有的学号所在的行取出来,学生表里会把学号0001行的数据取出来,成绩表里学号0001里有两行,取出的是两行数据。

第二步:

将两个表里取出来的数据进行合并,这里就是进行交叉联结了,交叉联结是将表中的每一行跟另一个表中的每一行两两合在一起,这里将学生表中的学号0001一行数据跟成绩表中的两行数据合并,最终产生了两行数据,合并以后的表呢就有了两张表中的全部信息。学号,姓名,出生日期,性别,课程号,成绩都放在了一张表中。


postgres 两个sql加减 两个sql结果相加_sql 两条数据 空值合并_08


SQL查询语句


-- 内联结SQL查询语句
Select a.学号,a.姓名,b.课程号
From student as a inner join score as b-----重点1:score as 
On a.学号=b.学号-- 重点on 在学号的基础上进行联结


postgres 两个sql加减 两个sql结果相加_sql 两条数据 空值合并_09


注意事项:From中使用了两张表

1、 表的别名,列名是为了区分是哪张表中的数据,需要在列名前加上表的别名跟点
2、 两个表中间inner join 就说明两张表通过inner join 进行内联结
3、 From子句中的关键字on 表示两张表是通过哪个列匹配产生关系的

3)左联结:left join

会将左侧表中的数据取出来,文氏图中左联结就是学生表中的部分


postgres 两个sql加减 两个sql结果相加_sql 列求和_10


如何进行运行的

第一步

左联结就是左侧的表作为主表,主表中的数据全部读取出来,也就是将学生表中的数据全部取出来,右边的表中只选出和左边表中相同学号的行也就是学号0001所在的两行数据

第二步

将两个表中取出的数据进行合并,如何合并:这里就要进行交叉联结了,因为学号0002在右边的表里没有对应的行,所以这一行对应的列的值就是空值

对应的SQL语句


-- 左联结left join SQL语句
Select a.学号,a.姓名,b.课程号
From student as a left join score as b
On a.学号=b.学号


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_11


左联结的基础上,我们再来看一个问题

图片中红色部 分的地方如何用SQL表示呢


postgres 两个sql加减 两个sql结果相加_sql 相加_12


这是在左联结中去掉了两个表中共同的地方,也就是两张表中的公共部分

SQL语句


-- 左联结中去掉公共部分
Select a.学号,a.姓名,b.课程号
From student as a left join score as b
On a.学号=b.学号
Where b.学号 is null;


postgres 两个sql加减 两个sql结果相加_sql 列求和_13


4)右联结:right join

右联结会将右侧表中的数据全部取出来,图片中文氏图画出了右联结是红色图的地方


postgres 两个sql加减 两个sql结果相加_sql 相加_14


第一步:两行表通过学号进行右联结,将右侧表的数据全部取出来

就是将成绩表里的数据全部取出来
左边表只选出和右边表相同学号的行

第二步:

将两张表中取出的数据合并:
如何合并:就是进行交叉联结了,因为学号0005在左边的表中没有相应的行,这一行对应的列是空值。

SQL语句


-- 右联结 right join SQL语句
Select a.学号,a.姓名,b.课程号
From student as a right join score as b
On a.学号=b.学号


postgres 两个sql加减 两个sql结果相加_sql 相加_15


在右联结的基础上,我们再来看一个问题,图片中红色部分应该如何用SQL表示呢

这是在右联结的基础上去掉了两个表中共同的地方,去掉了两张表中共同的地方


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_16


SQL语句


-- left join 右联结去掉相同列的SQL语句
Select a.学号,a.姓名,b.课程号
From student as a right join score as b
On a.学号=b.学号
Where a.学号 is null;


5)全联结:full join

全联结的查询结果会返回左表和右表中的所有行,当某行和另一个表中有匹配的时候两个行进行合并,如果某行和另一个表中没有匹配的行的时候,另一个表中没有的值用空值填充,在图片中看出了有两行空值,分别是左联结结果和右联结结果中的空值行,这样就会看到两个表中的数据都在联结结果中了,值得一提的是MySQL是不支持全联结的。

6)SQL联结


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_17


什么时候用哪种联结呢

当实际工作业务中想要生成固定行数的表单,或者特别说明了要哪一张表里的全部数据的时候,会使用左联结或右联结,其他情况都是用内联结获取两个表的公共部分。

3、 SQL运行顺序


postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表_18


三、 联结应用案例

如何用SQL解决业务问题

主要分为三个步骤

1、 翻译成大白话
2、 写出分析思路
3、 写出对应的SQL子句

案例问题1:查询所有学生的学号、姓名、选课数、总成绩

1、 翻译成大白话

1) 学号、姓名(学生表student)
2) 选课数(每个学生的选课数目):成绩表 score 按学号分组,对课程号计数count
3) 总成绩(每个学生的总成绩:成绩表score,
4) 按学号分组,对成绩求和sum

2、 写出分析思路

查询所有学生的学号、姓名、每个学生的选课数目、每个学生的总成绩
Select 查询结果【学号,姓名,选课数,总成绩】
From 从哪张表中查找数据【学生表student,成绩表score 两个表如何联结?通过学号 哪种联结?左联结】
Where 查询结果【没有】
Group by 分组【每个学生的选课数目:按学号分组,对课程号计数count)
每个学生的总成绩:按学号分组,对成绩求和sum)】
Having 对分组结果指定条件【没有】
Order by 对查询结果排序【没有】
Limit 从查询结果中取出指定行【没有】

3、 写出对应的SQL语句

-- 查询所有学生的学号、姓名、选课数、总成绩


-- 查询所有学生的学号、姓名、选课数、总成绩
Select 学号,姓名,count(课程号)as 选课数,sum(成绩)as总成绩
From student as a left join score as b
On a.学号=b.学号
Group by a.学号


postgres 两个sql加减 两个sql结果相加_sql 列求和_19


案例问题2:查询平均成绩大于85的所用学生的学号、姓名和平均成绩

1、 翻译成大白话

1. 查询出所有学生的学号,姓名,平均成绩,学号,
姓名(在学生表student),
平均成绩(每个学生的平均成绩);
在成绩表score,
按学号分组,平均成绩:avg(成绩)
2. 平均成绩>85

2、 写出分析思路

Select 查询结果【学号,姓名,平均成绩】
From 从哪张表中查找数据【学生表student,成绩表score 两个表如何关联?通过学号 用哪种联结?左联结】
Where 查询条件【没有】
Group by分组【每个学生的选课项目:按学号分组,对课程号计数count
每个学生的总成绩:按学号分组,对成绩求和sum】
Having 对分组结果指定条件【没有】
Order by 对查询结果排序【没有】
Limit 从查询结果中取出指定行【没有】;

3、 写出对应的SQL子句


-- 查询所有学生的学号、姓名、平均成绩
Select a.学号,a.姓名,avg(b.成绩)as 平均成绩
From student as a left join score as b
On a.学号=b.学号
Group by a.学号;


postgres 两个sql加减 两个sql结果相加_sql 列求和_20


查询平均成绩大于85的所有学生的学号、姓名和平均成绩


--  查询平均成绩大于85的所有学生的学号、姓名和平均成绩
Select a.学号,a.姓名,avg(b.成绩)as 平均成绩
From student as a left join score as b
On a.学号=b.学号
Group by a.学号
Having avg(b.成绩)>85;


postgres 两个sql加减 两个sql结果相加_sql 相加_21


案例问题:查询学生的选课情况:

学号,姓名,课程号,课程名称

1) 学号,姓名(学生表student)
2) 课程号,课程名称(课程表course)

SQL语句


-- 查询学生的选课情况
Select a.学号,a.姓名,c.课程号,c.课程名称
From student a inner join score b on a.学号=b.学号
Inner join course c on b.课程号=c.课程号;


postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表_22


四、 Case表达式

什么是Case表达式?

Case表达式可以帮助我们解决复杂的查询问题,case表达的作用就相当于进行一个条件判断的函数用来判断每一行是不是满足某个条件,图片中给出了case表达式的语句,里面的wen子句的判断表达式,来判断某行数据,是否符合某个条件,如果符合条件就运行后面的then子句,case表达式运行到此结束,就不会运行后面的when子句了,如果不符合条件就进入下一个when子句,如果知道最后面的when子句都没有找到合适的数据,那么就会运行else中的表达式

Case when<判断表达式 >then<表达式>

when<判断表达式 >then<表达式>

when<判断表达式 >then<表达式>

。。。。

Else<表达式>

End

案例:判断学生成绩是否及格


--判断学生成绩是否及格
Select 学号,课程号,成绩,
(case when 成绩>=60 then ‘及格’
When 成绩<60 then ‘不及格’
Else null
end)as 是否及格
from score;


postgres 两个sql加减 两个sql结果相加_sql 相加_23


案例:查询出每门课程的及格人数和不及格人数

1) 查询出每门课程的人数


--  查询出每门课程的人数
Select 课程号,Count(学号)as 人数
From score
Group by 课程号;


2) 查询出每门课程的及格人数和不及格人数


-- 查询出每门课程的及格人数和不及格人数
Select 课程号,
Sum(case when 成绩>=60 then 1 
else 0
end)as及格人数,
sum(case when 成绩<60 then 1
else 0
end)as 不及格人数
from score
group by 课程号;


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_24


case表达式注意事项

Case
when<判断表达式 >then<表达式>
when<判断表达式 >then<表达式>
when<判断表达式 >then<表达式>
。。。。
Else<表达式>………………else可以省略,默认为空值
End…………………….end不能省略不写的
Case表达式能写在SQL语句中任意语句中

case 表达式有什么用,什么时候比较好用呢

当有多种条件判断的时候使用case表达式了

案例:使用分段[100-85],[85-70],[70-60][<60]来统计各科成绩,分别统计:各分数段人数(成绩表score),课程号和课程名称(课程表course)


/*
使用分段[100-85],[85-70],[70-60][<60]来统计各科成绩,
分别统计:各分数段人数(成绩表score),课程号和课程名称(课程表course)
*/
Select a.课程号,b.课程名称,
Sum(case when 成绩 between 85 and 100
Then 1 else 0 end)as '[100-85]',
Sum(case when 成绩>=70 and 成绩<85
Then 1 else 0 end)as '[85-70]',
Sum(case when 成绩>=60 and 成绩<70
Then 1 else 0 end )as '[70-60]',
Sum(case when 成绩<60 Then 1 else 0 end)as '[<60]'
From score as a right join course as b
On a.课程号=b.课程号
Group by a.课程号,b.课程名称


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_25


当用多个列来分组时,这几个列的值全部相同才算一组

五、练习题 SQLzoo join


/*
1、第一個例子列出球員姓氏為'Bender'的入球數據。 * 表示列出表格的全部欄位,簡化了寫matchid, teamid, player, gtime語句。

修改此SQL以列出 賽事編號matchid 和球員名 player ,該球員代表德國隊Germany入球的。要找出德國隊球員,要檢查: teamid = 'GER'
*/
SELECT matchid,player
FROM goal 
WHERE teamid = 'GER'


postgres 两个sql加减 两个sql结果相加_sql 两条数据 空值合并_26


/*
2、由以上查詢,你可見Lars Bender's 於賽事 1012入球。.現在我們想知道此賽事的對賽隊伍是哪一隊。

留意在 goal 表格中的欄位 matchid ,是對應表格game的欄位id。我們可以在表格 game中找出賽事1012的資料。

只顯示賽事1012的 id, stadium, team1, team2
*/
SELECT id,stadium,team1,team2
FROM game
where id=1012


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_27


/*
3、我們可以利用JOIN來同時進行以上兩個步驟。

SELECT *
  FROM game JOIN goal ON (id=matchid)
語句FROM 表示合拼兩個表格game 和 goal的數據。語句 ON 表示如何找出 game中每一列應該配對goal中的哪一列 -- goal的 id 必須配對game的 matchid 。 簡單來說,就是 
ON (game.id=goal.matchid)

以下SQL列出每個入球的球員(來自goal表格)和場館名(來自game表格)

修改它來顯示每一個德國入球的球員名,隊伍名,場館和日期。
*/
SELECT y.player,y.teamid,x.stadium,x.mdate
  FROM game as x inner JOIN goal as y ON (x.id=y.matchid)
where teamid='GER'


postgres 两个sql加减 两个sql结果相加_sql 列求和_28


/*
4、使用上題相同的 JOIN語句,

列出球員名字叫Mario (player LIKE 'Mario%')有入球的 隊伍1 team1, 隊伍2 team2 和 球員名 player
*/
select x.team1,x.team2 ,y.player
FROM game as x inner JOIN goal as y ON (x.id=y.matchid)
where player like'Mario%'


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_29


/*
5、表格eteam 貯存了每一國家隊的資料,包括教練。你可以使用語句 goal JOIN eteam on teamid=id來合拼 JOIN 表格goal 到 表格eteam。

列出每場球賽中首10分鐘gtime<=10有入球的球員 player, 隊伍teamid, 教練coach, 入球時間gtime
*/
SELECT x.player, x.teamid,y.coach, x.gtime
  FROM goal as x inner join eteam as y on x.teamid=y.id
 WHERE gtime<=10


postgres 两个sql加减 两个sql结果相加_postgres 两个sql加减_30


/*
6、要合拼JOIN 表格game 和表格 eteam,你可以使用
game JOIN eteam ON (team1=eteam.id)
或
game JOIN eteam ON (team2=eteam.id)
注意欄位id同時是表格game 和表格 eteam的欄位,你要清楚指出eteam.id而不是只用id

列出'Fernando Santos'作為隊伍1 team1 的教練的賽事日期,和隊伍名。
*/
select x.mdate,y.teamname
from game as x inner join eteam as y on x.team1=y.id
where coach='Fernando Santos'


postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表_31


/*
7、列出場館 'National Stadium, Warsaw'的入球球員。
*/
select b.player
from game as a inner join goal as b  on a.id=b.matchid
where stadium='National Stadium, Warsaw'


postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表_32


/*
8、以下例子找出德國-希臘Germany-Greece 的八強賽事的入球
修改它,只列出全部賽事,射入德國龍門的球員名字。
*/
SELECT distinct(b.player)
  FROM game as a inner JOIN goal as b ON b.matchid = a.id 
    WHERE (a.team1='GER' or a.team2='GER') and b.teamid <>'GER'


postgres 两个sql加减 两个sql结果相加_sql 相加_33


/*
9、列出隊伍名稱 teamname 和該隊入球總數
COUNT and GROUP BY
*/
SELECT a.teamname, count(b.player)
  FROM eteam as a inner JOIN goal as b ON a.id=b.teamid
 group BY a.teamname


postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表_34


/*
10、列出場館名和在該場館的入球數字。
*/
select a.stadium,count(b.player)
from game as a inner join goal as b on a.id=b.matchid
group by a.stadium


postgres 两个sql加减 两个sql结果相加_sql 列求和_35


/*
11、每一場波蘭'POL'有參與的賽事中,列出賽事編號 matchid, 日期date 和入球數字。
*/
SELECT b.matchid,a.mdate, count(b.player)
  FROM game as a inner JOIN goal as b ON b.matchid = a.id 
 WHERE (a.team1 = 'POL' OR a.team2 = 'POL')
group by b.matchid


postgres 两个sql加减 两个sql结果相加_sql 相加_36


/*
12、每一場德國'GER'有參與的賽事中,列出賽事編號 matchid, 日期date 和德國的入球數字。
*/
select b.matchid,a.mdate,count(b.player)
from game as a inner join goal as b on a.id=b.matchid
where (a.team1='GER'or a.team2='GER') and b.teamid='GER'
group by b.matchid


postgres 两个sql加减 两个sql结果相加_sql 列求和_37


/*
13、List every match with the goals scored by each team as shown. This will use "CASE WHEN" which has not been explained in any previous exercises.
*/
SELECT a.mdate,a.team1,
  sum(CASE WHEN b.teamid=a.team1 THEN 1 ELSE 0 END) as score1,
  a.team2,
  sum(CASE WHEN b.teamid=a.team2 THEN 1 ELSE 0 END) as score2
  FROM game as a left JOIN goal as b ON b.matchid = a.id
 group by a.id,a.mdate,a.team1,a.team2
 order by a.mdate,a.id,a.team1,a.team2


postgres 两个sql加减 两个sql结果相加_sql 语句如何一个表变成另一个表_38