【数据库】SQL新手完整笔记(上)

学习时间:2020/4/17-24
使用:①廖雪峰SQL教程 ②SQL_菜鸟教程 ③《数据库系统原理》

SQL笔记-目录

  • 1、关系数据库概述
    • 1.1 笔记(课程)介绍
    • 1.2 关系数据库概述
      • 1.2.1 数据模型(3种)
      • 1.2.2 关系模型简介
      • 1.2.3 数据类型
      • 1.2.4 SQL——结构化查询语言
      • 1.2.5 SQL语法特点
    • 1.3 MySQL
    • 1.4 关系模型详解
      • 1.4.1 主键
      • 1.4.2 外键
        • 1.4.2.1 一对多关系
        • 1.4.2.2 多对多关系
        • 1.4.2.3 一对一关系
      • 1.4.3 索引
    • 1.5 SQL语言的注释方法
  • 2、查询数据
    • 2.1 基本查询
    • 2.2 条件查询
    • 2.3 投影查询
    • 2.4 排序
    • 2.5 分页查询
    • 2.6 聚合查询
    • 2.7 分组聚合查询
    • 2.8 多表查询(笛卡尔查询)
    • 2.9 连接查询
    • 2.9续 四种JOIN查询

1、关系数据库概述

1.1 笔记(课程)介绍

什么是SQL?

简单地说,SQL就是访问和处理关系数据库的计算机标准语言。也就是说,无论用什么编程语言(Java、Python、C++……)编写程序,只要涉及到操作关系数据库,比如,一个电商网站需要把用户和商品信息存入数据库,或者一个手机游戏需要把用户的道具、通关信息存入数据库,都必须通过SQL来完成。

现代程序离不开关系数据库,要使用关系数据库就必须掌握SQL。

注意: 廖雪峰教程讲解如何使用SQL操作数据库,以及一种最流行的开源数据库MySQL的基本安装和使用方法。

补充: NoSQL —— 非SQL的数据库

包括MongoDB、Cassandra、Dynamo等等,它们都不是关系数据库。有很多人鼓吹现代Web程序已经无需关系数据库了,只需要使用NoSQL就可以。但事实上,SQL数据库从始至终从未被取代过!!今天,SQL数据库仍然承担了各种应用程序的核心数据存储,而NoSQL数据库作为SQL数据库的补充,两者不再是二选一的问题,而是主从关系。所以,无论使用哪种编程语言,无论是Web开发、游戏开发还是手机开发,掌握SQL,是所有软件开发人员所必须的。

1.2 关系数据库概述

数据库是一门专门管理数据的软件。应用程序不需要自己管理数据,而是通过数据库软件提供的接口来读写数据。至于数据本身如何存储到文件,那是数据库软件的事情,应用程序自己并不关心。

1.2.1 数据模型(3种)

数据库按照数据结构来组织、存储和管理数据,实际上,数据库一共有三种模型:

1.层次模型
2.网状模型
3.关系模型

  1. 层次模型
    层次模型就是以“上下级”的层次关系来组织数据的一种方式,层次模型的数据结构看起来就像一颗树。
    【数据库】SQL新手完整笔记(上)_第1张图片
  2. 网状模型
    网状模型把每个数据节点和其他很多节点都连接起来,它的数据结构看起来就像很多城市之间的路网——
    【数据库】SQL新手完整笔记(上)_第2张图片
  3. 关系模型
    关系模型把数据看作是一个二维表格任何数据都可以通过行号+列号来唯一确定,它的数据模型看起来就是一个Excel表!
    【数据库】SQL新手完整笔记(上)_第3张图片
    基于关系模型的关系数据库,是当下的主流。

1.2.2 关系模型简介

这一块为简要介绍,在1.4里会更详细的介绍~

关系数据库的关系模型是基于数学理论建立的。

案例——以学校班级为例,一个班级的学生就可以用一个表格存起来,并且定义如下:
【数据库】SQL新手完整笔记(上)_第4张图片
其中,班级ID对应着另一个班级表:
在这里插入图片描述
通过给定一个班级名称,可以查到一条班级记录,根据班级ID,又可以查到多条学生记录,这样,二维表之间就通过ID映射建立了“一对多”关系。

如果我们把班级表分拆得细一点,例如,单独创建一个教师表:
【数据库】SQL新手完整笔记(上)_第5张图片
班级表只存储教师ID:
在这里插入图片描述
这样,一个班级总是对应一个教师,班级表和教师表就是“一对一”关系。

1.2.3 数据类型

对于一个关系表,除了定义每一列的名称外,还需要定义每一列的数据类型
【数据库】SQL新手完整笔记(上)_第6张图片
★通常来说,BIGINT能满足整数存储的需求,VARCHAR(N)能满足字符串存储的需求,这两种类型是使用最广泛的。

1.2.4 SQL——结构化查询语言

为啥要学SQL ?

SQL语句既可以查询数据库中的数据,也可以添加、更新和删除数据库中的数据,还可以对数据库进行管理和维护操作。不同的数据库,都支持SQL,这样,我们通过学习SQL这一种语言,就可以操作各种不同的数据库。

SQL的功能拓展 :

虽然SQL已经被ANSI组织定义为标准,不幸地是,各个不同的数据库对标准的SQL支持不太一致。并且,大部分数据库都在标准的SQL上做了扩展。也就是说,如果只使用标准SQL,理论上所有数据库都可以支持,但如果使用某个特定数据库的扩展SQL,换一个数据库就不能执行了。例如,Oracle把自己扩展的SQL称为PL/SQL,Microsoft把自己扩展的SQL称为T-SQL。各个数据库支持的各自扩展的功能,通常我们把它们称之为“方言”。

总的来说,SQL语言定义了这么几种操作数据库的能力:

DDL:Data Definition Language

DDL允许用户定义数据,也就是创建表、删除表、修改表结构这些操作。通常,DDL由数据库管理员执行。

DML:Data Manipulation Language

DML为用户提供添加、删除、更新数据的能力,这些是应用程序对数据库的日常操作。

DQL:Data Query Language

DQL允许用户查询数据,这也是通常最频繁的数据库日常操作。

1.2.5 SQL语法特点

SQL语言关键字不区分大小写!!!但是,针对不同的数据库,对于表名和列名,有的数据库区分大小写,有的数据库不区分大小写。同一个数据库,有的在Linux上区分大小写,有的在Windows上不区分大小写。

廖雪峰教程约定:
SQL关键字总是大写,以示突出,表名和列名均使用小写

1.3 MySQL

MySQL是目前 应用最广泛 的开源关系数据库。MySQL最早是由瑞典的MySQLAB公司开发,该公司在2008年被SUN公司收购,紧接着,SUN公司在2009年被Oracle公司收购,所以MySQL最终就变成了Oracle旗下的产品

和其他关系数据库有所不同的是,MySQL本身实际上只是一个SQL接口,它的内部还包含了多种数据引擎,如——
InnoDB:不知道应该采用哪种引擎,记住总是选择InnoDB就好了。

//警告:第一次安装的时候看错了下了32位,配置完以后setup程序就卡死了…求稳的话直接走廖雪峰的这个教程。

1.4 关系模型详解

关系数据库是建立在关系模型上的,而关系模型本质上就是若干个存储数据的二维表,可以把它们看作很多Excel表。

表的每一行称为记录(Record),记录是一个逻辑意义上的数据。

表的每一列称为字段(Column),同一个表的每一行记录都拥有相同的若干字段。

字段定义了数据类型(整型、浮点型、字符串、日期等),以及是否允许为NULL。注意NULL表示字段数据不存在。一个整型字段如果为NULL不表示它的值为0,同样的,一个字符串型字段为NULL也不表示它的值为空串’’。

通常情况下,字段应该避免允许为NULL。不允许为NULL可以简化查询条件,加快查询速度,也利于应用程序读取数据后无需判断是否为NULL。

关系数据库的表和表之间需要建立“一对多”,“多对一”和“一对一”的关系,这样才能够按照应用程序的逻辑来组织和存储数据。

在关系数据库中,关系是通过主键和外键来维护的

1.4.1 主键

一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。

对于关系表,有个很重要的约束:任意两条记录不能重复。即:
能够通过某个字段唯一区分出不同的记录,这个字段被称为主键

例如,假设我们把name字段作为主键,那么通过名字小明或小红就能唯一确定一条记录。但是,这么设定,就没法存储同名的同学了,因为插入相同主键的两条记录是不被允许的。

对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。

选取主键的一个基本原则是:不使用任何业务相关的字段作为主键。

(廖雪峰大佬)如果我们以学生的身份证号作为主键,似乎能唯一定位记录。然而,身份证号也是一种业务场景,如果身份证号升位了,或者需要变更,作为主键,不得不修改的时候,就会对业务产生严重影响。因此,身份证号、手机号、邮箱地址这些看上去可以唯一的字段,均不可用作主键。

★作为主键最好是完全业务无关的字段,我们一般把这个字段命名为id。常见的可作为id字段的类型有:

  1. 自增整数类型
    数据库会在插入数据时自动为每一条记录分配一个自增整数,这样我们就完全不用担心主键重复,也不用自己预先生成主键。对于大部分应用来说,通常自增类型的主键就能满足需求

廖:我们在students表中定义的主键也是BIGINT NOT NULL AUTO_INCREMENT类型。使用BIGINT自增类型则可以最多约922亿亿条记录!

  1. 全局唯一GUID类型
    使用一种全局唯一的字符串作为主键,类似8f55d96b-8acc-4636-8cb8-76bf8abc2f57。GUID算法通过网卡MAC地址、时间戳和随机数保证任意计算机在任意时间生成的字符串都是不同的,大部分编程语言都内置了GUID算法,可以自己预算出主键

~总结:应该使用BIGINT自增或者GUID类型。

关于联合主键——

关系数据库允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。对于联合主键,允许一列有重复,只要不是所有主键列都重复即可
【数据库】SQL新手完整笔记(上)_第7张图片
注意:没有必要的情况下,我们尽量不使用联合主键,因为它给关系表带来了复杂度的上升!!

最后补充:主键也不应该允许NULL。

1.4.2 外键

关系数据库通过外键可以实现一对多、多对多和一对一的关系。外键既可以通过数据库来约束,也可以不设置约束,仅依靠应用程序的逻辑来保证。

1.4.2.1 一对多关系

当我们用主键唯一标识记录时,我们就可以在students表中确定任意一个学生的记录:
【数据库】SQL新手完整笔记(上)_第8张图片
我们还可以在classes表中确定任意一个班级记录:
【数据库】SQL新手完整笔记(上)_第9张图片
但是我们如何确定students表的一条记录,例如,id=1的小明,属于哪个班级呢 ?????????

由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个classes的记录可以对应多个students表的记录。为了表达这种一对多的关系,我们需要在students表中加入一列class_id,让它的值与classes表的某条记录相对应:
【数据库】SQL新手完整笔记(上)_第10张图片
这样,我们就可以根据class_id这个列直接定位出一个students表的记录应该对应到classes的哪条记录。(理解!)

上述内容中: 在students表中,通过class_id的字段,可以把数据与另一张表关联起来,这种列称为外键。即class_id是外键!

外键并不是通过列名实现的。外键是通过定义外键约束实现的

ALTER TABLE students
ADD CONSTRAINT fk_class_id    #外键约束的名称
FOREIGN KEY (class_id)        #指定class_id作为外键
REFERENCES classes (id);      #指定了这个外键将关联到classes表的id列

通过定义外键约束,关系数据库可以保证无法插入无效的数据!即如果classes表不存在id=99的记录,students表就无法插入class_id=99的记录。

廖: 由于外键约束会降低数据库的性能,大部分互联网应用程序为了追求速度,并不设置外键约束,而是仅靠应用程序自身来保证逻辑的正确性。这种情况下,class_id仅仅是一个普通的列,只是它起到了外键的作用而已。

删除一个外键约束,也是通过ALTER TABLE实现的:

ALTER TABLE students
DROP FOREIGN KEY fk_class_id;

删除外键约束并没有删除外键这一列!删列是通过DROP COLUMN …实现的。

1.4.2.2 多对多关系

引入:通过一个表的外键关联到另一个表,我们可以定义出一对多关系。有些时候,还需要定义“多对多”关系。例如,一个老师可以对应多个班级,一个班级也可以对应多个老师,因此,班级表和老师表存在多对多关系。

★实现:多对多关系实际上是通过两个一对多关系实现的,即通过一个中间表,关联两个一对多关系,就形成了多对多关系——
【数据库】SQL新手完整笔记(上)_第11张图片
中间表teacher_class关联两个一对多关系:(理解!!)
【数据库】SQL新手完整笔记(上)_第12张图片
对上面的中间表的解释:
【数据库】SQL新手完整笔记(上)_第13张图片

1.4.2.3 一对一关系

一对一关系是指,一个表的记录对应到另一个表的唯一一个记录。例如,students表的每个学生可以有自己的联系方式,如果把联系方式存入另一个表contacts,我们就可以得到一个“一对一”关系。

引出疑问:为啥不给students表增加一个mobile列,这样就能合二为一了?
【数据库】SQL新手完整笔记(上)_第14张图片
如果业务允许,完全可以把两个表合为一个表。但是,有些时候,如果某个学生没有手机号,那么,contacts表就不存在对应的记录。实际上,一对一关系准确地说,是contacts表一对一对应students表

★作用——有些应用会把一个大表拆成两个一对一的表,目的是把经常读取和不经常读取的字段分开,以获得更高的性能! 例如:

把一个大的用户表分拆为用户基本信息表user_info和用户详细信息表user_profiles,大部分时候,只需要查询user_info表,并不需要查询user_profiles表,这样就提高了查询速度。

1.4.3 索引

查找记录的时候,想要获得非常快的速度,就需要使用索引。

索引是关系数据库中对某一列或多个列的值进行预排序的数据结构。通过使用索引,可以让数据库系统不必扫描整个表,而是直接定位到符合条件的记录,这样就大大加快了查询速度

例如,对于students表:
【数据库】SQL新手完整笔记(上)_第15张图片
如果要经常根据score列进行查询,就可以对score列创建索引

ALTER TABLE students           
ADD INDEX idx_score (score);  #创建名为idx_score,使用列score的索引

(索引名称是任意的)

引如果有多列,可以在括号里依次写上,例如:

ALTER TABLE students
ADD INDEX idx_name_score (name, score);

索引的效率取决于索引列的值是否散列,即该列的值如果越互不相同,那么索引效率越高。反过来,如果记录的列存在大量相同的值,例如gender列,大约一半的记录值是M,另一半是F,因此,对该列创建索引就没有意义!

注意使用索引的代价:索引的优点是提高了查询效率,缺点是在插入、更新和删除记录时,需要同时修改索引,因此,索引越多,插入、更新和删除记录的速度就越慢

对于主键,关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的,因为主键会保证绝对唯一。

关于唯一索引 —— UNIQUE

review:在设计关系数据表的时候,看上去唯一的列,例如身份证号、邮箱地址等,因为他们具有业务含义,因此不宜作为主键。

but:这些列根据业务要求,又具有唯一性约束:即不能出现两条记录存储了同一个身份证号。这个时候,就可以给该列添加一个唯一索引!!

例如,我们假设students表的name不能重复:

ALTER TABLE students
ADD UNIQUE INDEX uni_name (name); #通过UNIQUE关键字添加一个唯一索引

补充:也可以只对某一列添加一个唯一约束而不创建唯一索引——

ALTER TABLE students
ADD CONSTRAINT uni_name UNIQUE (name);

这种情况下,name列没有索引,但仍然具有唯一性保证!!!

//廖雪峰解释如何合理使用索引:

无论是否创建索引,对于用户和应用程序来说,使用关系数据库不会有任何区别。这里的意思是说,当我们在数据库中查询时,如果有相应的索引可用,数据库系统就会自动使用索引来提高查询效率,如果没有索引,查询也能正常执行,只是速度会变慢。因此,索引可以在使用数据库的过程中逐步优化。

1.5 SQL语言的注释方法

主要有三种。列举如下:

  1. 使用"#"进行单行注释
#单行注释
select * from students;
  1. 使用 "-- " ★注意,–后跟有一个空格!!
-- MySQL单行注释方法二
select * from students
  1. 多行注释: 使用/* */
/*
此处为注释....
哈哈哈哈嘿嘿嘿嘿
*/
select * from students;

2、查询数据

查询是关系数据库中最常见的操作。下面的内容将以一个students表和一个classes表为例,结构数据 如下:

——表1: students表
【数据库】SQL新手完整笔记(上)_第16张图片
——表2: classes表
【数据库】SQL新手完整笔记(上)_第17张图片

2.1 基本查询

要查询数据库表的数据,我们使用如下的SQL语句:

SELECT * FROM 表名;

# 例如:
SELECT * FROM students;

# 语句解释————————
SELECT是关键字,表示将要执行一个查询。
*表示“所有列”,FROM表示将要从哪个表查询,本例中是students表。

例子的显示结果即为“表1”。SELECT查询的结果是一个二维表

下面的语句可以显示“表2” ,原理同上:

SELECT * FROM classes;

★ SELECT语句其实并不要求一定要有FROM子句,例如:

SELECT 100+400;
 ####
 输出结果:
 100+400
 500

不带FROM子句的SELECT语句有一个有用的用途——用来判断当前到数据库的连接是否有效。许多检测工具会执行一条 SELECT 1; 来测试数据库连接。

2.2 条件查询

SELECT语句可以通过WHERE条件来设定查询条件,查询结果是满足查询条件的记录。

例如,要指定条件“分数在80分或以上的学生”,写成WHERE条件就是:

SELECT * FROM students WHERE score >= 80;

  ####
  语句解释——————
  WHERE关键字后面的score >= 80就是条件。
  score是列名,该列存储了学生的成绩,
  因此 score >= 80就筛选出了指定条件的记录

即:标准格式为:

SELECT * FROM 表名 WHERE 条件表达式 ;

★ 注意:条件表达式可以用<条件1> AND <条件2>表达同时满足条件1并且满足条件2 !! 例如:

SELECT * FROM students WHERE score >= 80 AND gender = 'M';

  ####
  同时满足:①成绩大于等于80  ②男性  两个条件。

★ 除了“AND”以外,还可以“OR” ——

SELECT * FROM students WHERE score >= 80 OR gender = 'M';

  ####
  多个条件满足一个即可。

★ 还可以 NOT <条件> ,表示“不符合该条件”的记录——

SELECT * FROM students WHERE NOT class_id = 2;

####
查询:不是2班的学生
————————————————————————————————————————————————————————————
补充————
上述NOT条件
NOT class_id = 2
其实等价于
class_id <> 2

★ 要组合三个或者更多的条件,就需要用小括号()表示如何进行条件运算。例如,编写一个复杂的条件:分数在80以下或者90以上,并且是男生:

SELECT * FROM students WHERE (score < 80 OR score > 90) AND gender = 'M';

加上小括号防止优先级出错!!

补充:
写条件时,如果是相等的条件,写一个等号即可!!

【数据库】SQL新手完整笔记(上)_第18张图片

2.3 投影查询

使用SELECT * FROM <表名> WHERE <条件>可以选出表中的若干条记录。我们注意到返回的二维表结构和原表是相同的,即结果集的所有列与原表的所有列都一一对应。

而 如果只需要返回特定的列:
可以用SELECT 列1, 列2, 列3 FROM …,让结果集仅包含指定列。这种操作称为投影查询。

SELECT id, score, name FROM students;

  ####
  代码解释:  从students表中返回id、score和name这三列。
             结果集的列的顺序和原表可以不一样!

★ 使用SELECT 列1, 列2, 列3 FROM …时,还可以给每一列起个别名,这样,结果集的列名就可以与原表的列名不同。它的语法是SELECT 列1 别名1, 列2 别名2, 列3 别名3 FROM …。 例如:

SELECT id, score points, name FROM students;

 ####SELECT语句将列名score重命名为points,而id和name列名保持不变。

2.4 排序

在使用SELECT查询时,查询结果集通常是按照id排序的,也就是根据主键排序。如果要根据其它条件来排序,可以加上ORDER BY子句

SELECT id, name, gender, score FROM students ORDER BY score;

 ####
 按照成绩从低到高进行排序。(默认就是升序排列。)

如果要反过来,按照成绩从高到底排序,我们可以加上DESC表示“倒序”

SELECT id, name, gender, score FROM students ORDER BY score DESC;

  ####
  按score从高到低。

★ 如果score列有相同的数据,要进一步排序,可以继续添加列名。例如:

SELECT id, name, gender, score FROM students ORDER BY score DESC, gender;

  ####
  先按score列倒序,如果有相同分数的,再按gender列排序。

★ 如果有WHERE子句,那么ORDER BY子句要放到WHERE子句后面。例如,查询一班的学生成绩,并按照倒序排序:

SELECT id, name, gender, score
FROM students
WHERE class_id = 1
ORDER BY score DESC;

上例中,结果集仅包含符合WHERE条件的记录,并按照ORDER BY的设定排序。

2.5 分页查询

思想:使用SELECT查询时,如果结果集数据量很大,比如几万行数据,放在一个页面显示的话数据量太大,不如分页显示,每次显示100条。要实现分页功能,实际上就是从结果集中显示第1-100条记录作为第1页,显示第101-200条记录作为第2页,以此类推。因此,分页实际上就是从结果集中“截取”出第M~N条记录。

实现子句:

LIMIT <M> OFFSET <N>

LIMIT 3 OFFSET 0
 ####
 对结果集从0号记录开始,最多取3条!

★ LIMIT 15 OFFSET 30 解释:
offset是偏移的意思 默认是0 偏移30的长度 截取长度是15 所以拿到的是第31-46 左闭右开

注意SQL记录集的索引从0开始。

★ 总结:分页查询需要先确定每页的数量和当前页数,然后确定LIMIT和OFFSET的值。即——

首先要确定每页需要显示的结果数量pageSize(这里是3行),然后根据当前页的索引pageIndex(即当前页的页码,从1开始),确定LIMIT和OFFSET应该设定的值:
在这里插入图片描述
注意:OFFSET超过了查询的最大数量并不会报错,而是得到一个空的结果集

其它:

① 在MySQL中,LIMIT 15 OFFSET 30还可以简写成LIMIT 30, 15。
② 使用LIMIT M OFFSET N分页时,随着N越来越大,查询效率也会越来越低。

2.6 聚合查询

对于统计总数、平均数这类计算,SQL提供了专门的聚合函数,使用聚合函数进行查询,就是聚合查询。

使用SQL内置的COUNT()函数查询students表一共有多少条记录:

SELECT COUNT(*) FROM students;

注意——COUNT(*)COUNT(id)实际上是一样的效果。

Output

COUNT(*)
10

注:聚合的计算结果虽然是一个数字,但查询的结果仍然是一个二维表,只是这个二维表只有一行一列,并且列名是COUNT(*)。

★ 通常,使用聚合查询时,我们应该给列名设置一个别名,便于处理结果:

SELECT COUNT(*) num FROM students;
# 起名:设置列名为num

Output

num
10

聚合查询同样可以使用WHERE条件,因此我们可以方便地统计出有多少男生、多少女生、多少80分以上的学生等。例如:

查询男生总人数:

SELECT COUNT(*) boys FROM students WHERE gender = 'M';

Output

boys
5

聚合函数汇总
【数据库】SQL新手完整笔记(上)_第19张图片
其中,MAX、MIN也可计算字符类型并返回。

例:统计男生的平均成绩

SELECT AVG(score) boys_average FROM students WHERE gender = 'M';

Output

boys_average
81.4

补充: 如果聚合查询的WHERE条件没有匹配到任何行,COUNT()会返回0,而SUM()、AVG()、MAX()和MIN()会返回NULL。

2.7 分组聚合查询

如果我们要统计一班的学生数量,我们知道,可以用SELECT COUNT(*) num FROM students WHERE class_id = 1;。如果要继续统计二班、三班的学生数量,难道必须不断修改WHERE条件来执行SELECT语句吗?(愚)

对于聚合查询,SQL还提供了“分组聚合”的功能。例如下面的聚合查询:

SELECT COUNT(*) num FROM students GROUP BY class_id;

Output

num
4
3
3

执行这个查询,COUNT()的结果不再是一个,而是3个,这是因为,GROUP BY子句指定了按class_id分组,因此,执行该SELECT语句时,会把class_id相同的列先分组,再分别计算,因此,得到了3行结果。

但是这3行结果分别是哪三个班级的,不好看出来,所以我们可以把class_id列也放入结果集中:

SELECT class_id, COUNT(*) num FROM students GROUP BY class_id;

Output

class_id num
1 4
2 3
3 3

警告: 聚合查询的列中,只能放入分组的列!

也可以使用多个列进行分组
例题:统计各班的男生和女生人数——

SELECT class_id, gender, COUNT(*) num FROM students GROUP BY class_id, gender;

Output

class_id gender num
1 M 2
1 F 2
2 F 1
2 M 2
3 F 2
3 M 1

2.8 多表查询(笛卡尔查询)

SELECT查询不但可以从一张表查询数据,还可以从多张表同时查询数据。例如:同时从students表和classes表的“乘积”,即查询数据——

SELECT * FROM students, classes;

查询的结果也是一个二维表,它是students表和classes表的“乘积”,即students表的每一行与classes表的每一行都两两拼在一起返回。结果集的列数是students表和classes表的列数之和,行数是students表和classes表的行数之积。

设置列的别名——防止不同表的列重名冲突

SELECT
    students.id sid,
    students.name,
    students.gender,
    students.score,
    classes.id cid,
    classes.name cname
FROM students, classes;

输出的列名依次为——
sid name gender score cid cname

上片代码可简写为:

SELECT
    s.id sid,
    s.name,
    s.gender,
    s.score,
    c.id cid,
    c.name cname
FROM students s, classes c;

★ 多表查询也是可以添加WHERE条件的!

例子——

SELECT
    s.id sid,
    s.name,
    s.gender,
    s.score,
    c.id cid,
    c.name cname
FROM students s, classes c
WHERE s.gender = 'M' AND c.id = 1;

output
【数据库】SQL新手完整笔记(上)_第20张图片

2.9 连接查询

连接查询是另一种类型的多表查询。连接查询对多个表进行JOIN运算,简单地说,就是先确定一个主表作为结果集,然后,把其他表的行有选择性地“连接”在主表结果集上。

例题
假设我们希望结果集同时包含所在班级的名称,students表只有class_id列,缺少对应班级的name列。name列在另一个表中,怎么调用呢?

思路——存放班级名称的name列存储在classes表中,只有根据students表的class_id,找到classes表对应的行,再取出name列,就可以获得班级名称。

实现—— //内连接(INNER JOIN)实现

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
INNER JOIN classes c
ON s.class_id = c.id;

注意 INNER JOIN查询 的写法是:

  1. 先确定主表,仍然使用FROM <表1>的语法;
  2. 再确定需要连接的表,使用INNER JOIN <表2>的语法;
  3. 然后确定连接条件,使用ON <条件…>,这里的条件是s.class_id = c.id,表示students表的class_id列与classes表的id列相同的行需要连接
  4. 可选:加上WHERE子句、ORDER BY等子句。

上面例题的外连接实现(OUTER JOIN) ——

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
RIGHT OUTER JOIN classes c
ON s.class_id = c.id;

★ 内连接 vs 外连接 ★

先说明:OUTER JOIN 包含 RIGHT OUTER JOIN 、LEFT OUTER JOIN 、FULL OUTER JOIN。

INNER JOIN只返回同时存在于两张表的行数据,由于students表的class_id包含1,2,3,classes表的id包含1,2,3,4,所以,INNER JOIN根据条件s.class_id = c.id返回的结果集仅包含1,2,3。

RIGHT OUTER JOIN返回右表都存在的行。如果某一行仅在右表存在,那么结果集就会以NULL填充剩下的字段

LEFT OUTER JOIN则返回左表都存在的行。

FULL OUTER JOIN,它会把两张表的所有记录全部选择出来,并且,自动把对方不存在的列填充为NULL。

2.9续 四种JOIN查询

假设查询语句是:

SELECT ... FROM tableA ??? JOIN tableB ON tableA.column1=tableB.column2;

注:下述例子中,把tableA看作左表,把tableB看成右表

  1. INNER JOIN是选出两张表都存在的记录:
    【数据库】SQL新手完整笔记(上)_第21张图片
  2. LEFT OUTER JOIN是选出左表存在的记录:
    【数据库】SQL新手完整笔记(上)_第22张图片
  3. RIGHT OUTER JOIN是选出右表存在的记录:
    【数据库】SQL新手完整笔记(上)_第23张图片
    4.FULL OUTER JOIN则是选出左右表都存在的记录:
    【数据库】SQL新手完整笔记(上)_第24张图片

熟知4种JOIN查询的覆盖范围,就能知道什么时候该使用哪种了!

总结:
①JOIN查询需要先确定主表,然后把另一个表的数据“附加”到结果集上。
②INNER JOIN是最常用的一种JOIN查询。
③JOIN查询仍然可以使用WHERE条件和ORDER BY排序。

—————————————————————————————————
SQL完整笔记_上篇(
下篇请点击跳转:SQL完整笔记(下)

你可能感兴趣的:(CS,sql,数据库,oracle,mysql,关系模型)