MySQL
数据库基本概念
(1)数据
所谓数据(Data)是指对客观事物进行描述并可以鉴别的符号,这些符号是可识别的、抽象的。它不仅仅指狭义上的数字,而是有多种表现形式:字母、文字、文本、图形、音频、视频等。现在计算机存储和处理的数据范围十分广泛,而描述这些数据的符号也变得越来越复杂了。
(2)数据库
数据库(Database,DB)指的是以一定格式存放、能够实现多个用户共享、与应用程序彼此独立的数据集合。
(3)数据库管理系统
数据库管理系统(Database Management System,DBMS)是用来定义和管理数据的软件。如何科学的组织和存储数据,如何高效的获取和维护数据,如何保证数据的安全性和完整性,这些都需要靠数据库管理系统完成。目前,比较流行的数据库管理系统有:Oracle、MySQL、SQL Server、DB2等。
(4)数据库应用程序
数据库应用程序(Database Application System,DBAS)是在数据库管理系统基础上,使用数据库管理系统的语法,开发的直接面对最终用户的应用程序,如学生管理系统、人事管理系统、图书管理系统等。
(5)数据库管理员
数据库管理员(Database Administrator,DBA)是指对数据库管理系统进行操作的人员,其主要负责数据库的运营和维护。
(6)最终用户
最终用户(User)指的是数据库应用程序的使用者。用户面向的是数据库应用程序(通过应用程序操作数据),并不会直接与数据库打交道。
(7) 数据库系统
数据库系统(Database System,DBS)一般是由数据库、数据库管理系统、数据库应用程序、数据库管理员和最终用户构成。其中DBMS是数据库系统的基础和核心。
数据库类型
数据库经过几十年的发展,出现了多种类型。根据数据的组织结构不同,主要分为网状数据库、层次数据库、关系型数据库和非关系型数据库四种。目前最常见的数据库模型主要是:关系型数据库和非关系型数据库。
- 关系型数据库
关系型数据库模型是将复杂的数据结构用较为简单的二元关系(二维表)来表示,如图1-4所示。在该类型数据库中,对数据的操作基本上都建立在一个或多个表格上,我们可以采用结构化查询语言(SQL)对数据库进行操作。关系型数据库是目前主流的数据库技术,其中具有代表性的数据库管理系统有:Oracle、DB2、SQL Server、MySQL等。
PS:关系=二维表
- 非关系型数据库NOSQL
NOSQL(Not Only SQL)泛指非关系型数据库。关系型数据库在超大规模和高并发的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题。NOSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。常见的非关系型数据库管理系统有Memcached、MongoDB,redis,HBase等。
- 常见的关系型数据库
虽然非关系型数据库的优点很多,但是由于其并不提供SQL支持、学习和使用成本较高并且无事务处理,所以本书的重点是关系型数据库。下面我们将介绍一下常用的关系型数据库管理系统。
虽然非关系型数据库的优点很多,但是由于其并不提供SQL支持、学习和使用成本较高并且无事务处理,所以本书的重点是关系型数据库。下面我们将介绍一下常用的关系型数据库管理系统。
-
Oracle
Oracle数据库是由美国的甲骨文(Oracle)公司开发的世界上第一款支持SQL语言的关系型数据库。经过多年的完善与发展,Oracle数据库已经成为世界上最流行的数据库,也是甲骨文公司的核心产品。
Oracle数据库具有很好的开放性,能在所有的主流平台上运行,并且性能高、安全性高、风险低;但是其对硬件的要求很高、管理维护和操作比较复杂而且价格昂贵,所以一般用在满足对银行、金融、保险等行业大型数据库的需求上。 -
DB2
DB2是IBM公司著名的关系型数据库产品。DB2无论稳定性,安全性,恢复性等等都无可挑剔,而且从小规模到大规模的应用都可以使用,但是用起来非常繁琐,比较适合大型的分布式应用系统。 -
SQL Server
SQL Server是由Microsoft开发和推广的关系型数据库,SQL Server的功能比较全面、效率高,可以作为中型企业或单位的数据库平台。SQL Server可以与Windows操作系统紧密继承,无论是应用程序开发速度还是系统事务处理运行速度,都能得到大幅度提升。但是,SQL Server只能在Windows系统下运行,毫无开放性可言。 -
MySQL
MySQL是一种开放源代码的轻量级关系型数据库,MySQL数据库使用最常用的结构化查询语言(SQL)对数据库进行管理。由于MySQL是开放源代码的,因此任何人都可以在General Public License的许可下下载并根据个人需要对其缺陷进行修改。
由于MySQL数据库体积小、速度快、成本低、开放源码等优点,现已被广泛应用于互联网上的中小型网站中,并且大型网站也开始使用MySQL数据库,如网易、新浪等。
- MySQL介绍
MySQL数据库最初是由瑞典MySQL AB公司开发,2008年1月16号被Sun公司收购。2009年,SUN又被Oracle收购。MySQL是目前IT行业最流行的开放源代码的数据库管理系统,同时它也是一个支持多线程高并发多用户的关系型数据库管理系统。MySQL之所以受到业界人士的青睐,主要是因为其具有以下几方面优点:
-
开放源代码
MySQL最强大的优势之一在于它是一个开放源代码的数据库管理系统。开源的特点是给予了用户根据自己需要修改DBMS的自由。MySQL采用了General Public License,这意味着授予用户阅读、修改和优化源代码的权利,这样即使是免费版的MySQL的功能也足够强大,这也是为什么MySQL越来越受欢迎的主要原因。 -
跨平台
MySQL可以在不同的操作系统下运行,简单地说,MySQL可以支持Windows系统、UNIX系统、Linux系统等多种操作系统平台。这意味着在一个操作系统中实现的应用程序可以很方便地移植到其他的操作系统下。 -
轻量级
MySQL的核心程序完全采用多线程编程,这些线程都是轻量级的进程,它在灵活地为用户提供服务的同时,又不会占用过多的系统资源。因此MySQL能够更快速、高效的处理数据。 -
成本低
MySQL分为社区版和企业版,社区版是完全免费的,而企业版是收费的。即使在开发中需要用到一些付费的附加功能,价格相对于昂贵的Oracle、DB2等也是有很大优势的。其实免费的社区版也支持多种数据类型和正规的SQL查询语言,能够对数据进行各种查询、增加、删除、修改等操作,所以一般情况下社区版就可以满足开发需求了,而对数据库可靠性要求比较高的企业可以选择企业版。
另外,PHP中提供了一整套的MySQL函数,对MySQL进行了全方位的强力支持。
总体来说,MySQL是一款开源的、免费的、轻量级的关系型数据库,其具有体积小、速度快、成本低、开放源码等优点,其发展前景是无可限量的。
PS:社区版与企业版主要的区别是:
PS:社区版与企业版主要的区别是:
- 社区版包含所有MySQL的最新功能,而企业版只包含稳定之后的功能。换句话说,社区版可以理解为是企业版的测试版。
- 2.MySQL官方的支持服务只是针对企业版,如果用户在使用社区版时出现了问题,MySQL官方是不负责任的。
SQL语言入门
我们都知道,数据库管理人员(DBA)通过数据库管理系统(DBMS)可以对数据库(DB)中的数据进行操作,但具体是如何操作的呢?这就涉及到我们本节要讲的SQL语言。
SQL(Structured Query Language)是结构化查询语言的简称,它是一种数据库查询和程序设计语言,同时也是目前使用最广泛的关系型数据库操作语言。在数据库管理系统中,使用SQL语言来实现数据的存取、查询、更新等功能。SQL是一种非过程化语言,只需提出“做什么”,而不需要指明“怎么做”。
SQL是由IBM公司在1974~1979年之间根据E.J.Codd发表的关系数据库理论为基础开发的,其前身是“SEQUEL”,后更名为SQL。由于SQL语言具有集数据查询、数据操纵、数据定义和数据控制功能于一体,类似自然语言、简单易用以及非过程化等特点,得到了快速的发展,并于1986年10月,被美国国家标准协会(American National Standards Institute,ANSI)采用为关系数据库管理系统的标准语言,后为国际标准化组织(International Organization for Standardization,ISO)采纳为国际标准。
SQL语言分为五个部分:
数据查询语言(Data Query Language,DQL):DQL主要用于数据的查询,其基本结构是使用SELECT子句,FROM子句和WHERE子句的组合来查询一条或多条数据。
数据操作语言(Data Manipulation Language,DML):DML主要用于对数据库中的数据进行增加、修改和删除的操作,其主要包括:
-
INSERT:增加数据
-
UPDATE:修改数据
-
DELETE:删除数据
数据定义语言(Data Definition Language,DDL):DDL主要用针对是数据库对象(数据库、表、索引、视图、触发器、存储过程、函数)进行创建、修改和删除操作。其主要包括:
数据控制语言(Data Control Language,DCL):DCL用来授予或回收访问 数据库的权限,其主要包括:
-
GRANT:授予用户某种权限
-
REVOKE:回收授予的某种权限
事务控制语言(Transaction Control Language,TCL):TCL用于数据库的事务管理。其主要包括:
-
START TRANSACTION:开启事务
-
COMMIT:提交事务
-
ROLLBACK:回滚事务
-
SET TRANSACTION:设置事务的属性
MySQL配置my.ini(默认参数)
安装目录的配置文件C:\ProgramData\MySQL\MySQL Server 8.0
[client]
#设置mysql客户端默认字符集 default-character-set=utf8 [mysql]
#设置mysql客户端默认字符集 default-character-set=utf8
user = 'root' password = '123' [mysqld]
² port=3306:监听端口是3306
² basedir="C:/Program Files/MySQL/MySQL Server 8.0/":软件安装位置
² datadir=C:/ProgramData/MySQL/MySQL Server 8.0/Data:数据文件夹位置
² default_authentication_plugin=caching_sha2_password:默认验证插件
² default-storage-engine=INNODB:默认存储引擎
数据库表列类型
1.整数类型
MySQL支持选择在该类型关键字后面的括号内指定整数值的显示宽度(例如,INT(4))。显示宽度并不限制可以在列内保存的值的范围,也不限制超过列的指定宽度的值的显示
主键自增:不使用序列,通过auto_increment,要求是整数类型
2.浮点数类型
需要注意的是与整数类型不一样的是,浮点数类型的宽度不会自动扩充。 score double(4,1)
score double(4,1)--小数部分为1位,总宽度4位,并且不会自动扩充。
3.字符串类型
CHAR和VARCHAR类型相似,均用于存于较短的字符串,主要的不同之处在于存储方式。CHAR类型长度固定,VARCHAR类型的长度可变。
因为VARCHAR类型能够根据字符串的实际长度来动态改变所占字节的大小,所以在不能明确该字段具体需要多少字符时推荐使用VARCHAR类型,这样可以大大地节约磁盘空间、提高存储效率。
CHAR和VARCHAR表示的是字符的个数,而不是字节的个数
4.日期和时间类型
TIMESTEMP类型的数据指定方式与DATETIME基本相同,两者的不同之处在于以下几点:
(1) 数据的取值范围不同,TIMESTEMP类型的取值范围更小。
(2) 如果我们对TIMESTAMP类型的字段没有明确赋值,或是被赋与了NULL值,MySQL会自动将该字段赋值为系统当前的日期与时间。
(3) TIMESTEMP类型还可以使用CURRENT_TIMESTAMP来获取系统当前时间。
(4) TIMESTEMP类型有一个很大的特点,那就是时间是根据时区来显示的。例如,在东八区插入的TIMESTEMP数据为2017-07-11 16:43:25,在东七区显示时,时间部分就变成了15:43:25,在东九区显示时,时间部分就变成了17:43:25。
数据库的操作
1 增: create database 数据库名 charset utf8 创建一个库, 可以指定字符集
2 删: drop database 数据库名 删除某个库
3 改: alter database 库名 charset utf8 修改库的字符集
4 查: show databases 查看数据库中所有的库
show create database 库名 查看单独某个库的信息
select database() 查看当前所在的是哪个库
表的操作
表的介绍
数据库可以将复杂的数据结构用较为简单的二维表来表示。
每个表都是由若干行和列组成的,在数据库中表中的行被称为记录,表中的列被称为是这些记录的字段。
创建表
创建表: create table 表名( 字段名1 类型[(宽度) 约束条件], 字段名2 类型[(宽度) 约束条件], 字段名3 类型[(宽度) 约束条件] );
#注意:
1. 在同一张表中,字段名是不能相同
2. 宽度和约束条件可选、非必须,宽度指的就是字段长度约束,例如:char(10)里面的10
3. 字段名和类型是必须的
修改表 alter table
增: 增加字段 alter table book add price varchar(30);
删: 删除字段 alter table 表名 drop 字段名;
删除表 drop table t1
改: 修改表名 alter table 旧表名 rename 新表名 修改字段属性
alter table 表名 modify 字段名 数据类型 [完整性约束条件…];
alter table t1 modify name char(3) 将name字段的char长度改为3
alter table 表名 change 旧字段名 新字段名 旧数据类型 [完整性约束条件…];
#change比modify还多了个改名字的功能,这一句是只改了一个字段名
修改列名
1、在oracle数据库中: ALTER TABLE 表名 RENAME COLUMN 列名 TO 新列名。
2、在sqlserver数据库中:exec sp_rename '[表名].[列名]‘,’[表名].[新列名]'。
3、在mysql数据库中:ALTER TABLE 表名 CHANGE 列名 新列名 列类型
查: show tables 查看当前库中所有的表
show create table t1 查看表的创建信息
describe t1 简写(desc t1) 查看表结构
-- 查看数据:
select * from t_student;
-- 修改表的结构:
-- 增加一列:
alter table t_student add score double(5,2) ; -- 5:总位数 2:小数位数
update t_student set score = 123.5678 where sno = 1 ;
-- 增加一列(放在最前面)
alter table t_student add score double(5,2) first;
-- 增加一列(放在sex列的后面)
alter table t_student add score double(5,2) after sex;
-- 删除一列:
alter table t_student drop score;
-- 修改一列:
alter table t_student modify score float(4,1); -- modify修改是列的类型的定义,但是不会改变列的名字
alter table t_student change score score1 double(5,1); -- change修改列名和列的类型的定义
-- 删除表:
drop table t_student;
-- 创建数据库表:
create table t_student(
sno int(6), -- 6显示长度
sname varchar(5), -- 5个字符
sex char(1),
age int(3),
enterdate date,
classname varchar(10),
email varchar(15)
);
-- 查看表的结构:展示表的字段详细信息
desc t_student;
-- 查看表中数据:
select * from t_student;
-- 查看建表语句:
show create table t_student;
CREATE TABLE `t_student` (
`sno` int DEFAULT NULL,
`sname` varchar(5) DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`age` int DEFAULT NULL,
`enterdate` date DEFAULT NULL,
`classname` varchar(10) DEFAULT NULL,
`email` varchar(15) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
行记录操作
增加数据 insert into
- 插入完整数据(顺序插入)
语法一: INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n); #指定字段来插入数据,插入的值要和你前面的字段相匹配
语法二: INSERT INTO 表名 VALUES (值1,值2,值3…值n); #不指定字段的话,就按照默认的几个字段来插入数据
-
指定字段插入数据 语法: INSERT INTO 表名(字段1,字段2,字段3…) VALUES (值1,值2,值3…);
-
插入多条记录 语法:#插入多条记录用逗号来分隔 INSERT INTO 表名 VALUES (值1,值2,值3…值n), (值1,值2,值3…值n), (值1,值2,值3…值n);
-
插入查询结果 语法: INSERT INTO 表名(字段1,字段2,字段3…字段n) SELECT (字段1,字段2,字段3…字段n) FROM 表2WHERE …; #将从表2里面查询出来的结果来插入到我们的表中,但是注意查询出来的数据要和我们前面 指定的字段要对应好
-- 在t_student数据库表中插入数据:
insert into t_student values (1,'张三','男',18,'2022-5-8','软件1班','123@126.com');
insert into t_student values (10010010,'张三','男',18,'2022-5-8','软件1班','123@126.com');
insert into t_student values (2,'张三','男',18,'2022.5.8','软件1班','123@126.com');
insert into t_student values (2,"张三",'男',18,'2022.5.8','软件1班','123@126.com');
insert into t_student values (7,"张三",'男',18,now(),'软件1班','123@126.com');
insert into t_student values (9,"易烊千玺",'男',18,now(),'软件1班','123@126.com');
insert into t_student (sno,sname,enterdate) values (10,'李四','2023-7-5');
更新(修改)数据UPDATE
UPDATE 表名 SET 字段1=值1,字段2=值2, WHERE ......;
update book set price while id=1;
#注意语法,可以同时来修改多个值,用逗号分隔 #更改哪些数据,通过where条件来定位到符合条件的数据
-- 修改表中数据
update t_student set sex = '女' ;
update t_student set sex = '男' where sno = 10 ;
UPDATE T_STUDENT SET AGE = 21 WHERE SNO = 10;
update t_student set CLASSNAME = 'java01' where sno = 10 ;
update t_student set CLASSNAME = 'JAVA01' where sno = 9 ;
update t_student set age = 29 where classname = 'java01';
删除数据DELETE
语法: DELETE FROM 表名 WHERE CONITION;
#删除符合条件的一些记录 DELETE FROM 表名;不加where条件,将表里面所有的内容都删掉,不能将自增字段置零
truncate 表名 清空所有的内容,一般我们用truncate ,能够将自增字段为零
-- 删除操作:
delete from t_student where sno = 2;
truncate t_student;

查询数据SELECT
单表查询的语法
#select * from,
这个select * 指的是要查询所有字段的数据。
SELECT distinct 字段1,字段2... FROM 库名.表名
WHERE 条件 跟的是你的查询条件 GROUP BY field(字段)
#分组 HAVING 筛选
#过滤 ORDER BY field(字段)
#排序 LIMIT #限制条数
from顺序
1.找到表:from where
2.拿着where指定的约束条件,去文件/表中取出一条条记录 group by
3.将取出的一条条记录进行分组group by,如果没有group by,则整体作为一组 having
4.将分组的结果进行having过滤 select
5.执行select distinct
6.去重 order by
7.将结果按条件排序:order by limit
8.限制结果的显示条数
表的完整性约束
为防止不符合规范的数据存入数据库,在用户对数据进行插入、修改、删除等操作时,MySQL提供了一种机制来检查数据库中的数据是否满足规定的条件,以保证数据库中数据的准确性和一致性,这种机制就是完整性约束。
MySQL中主要支持以下几种种完整性约束,如表所示。 其中Check约束是MySQL8中提供的支持。
约束条件 | 约束描述 |
---|---|
PRIMARY KEY | 主键约束,约束字段的值可唯一地标识对应的记录 |
NOT NULL | 非空约束,约束字段的值不能为空 |
UNIQUE | 唯一约束,约束字段的值是唯一的 |
CHECK | 检查约束,限制某个字段的取值范围 |
DEFAULT | 默认值约束,约束字段的默认值 |
AUTO_INCREMENT | 自动增加约束,约束字段的值自动递增 |
FOREIGN KEY | 外键约束,约束表与表之间的关系 |
1.主键约束
主键约束(PRIMARY KEY,缩写PK),是数据库中最重要的一种约束,其作用是约束表中的某个字段可以唯一标识一条记录。因此,使用主键约束可以快速查找表中的记录。就像人的身份证、学生的学号等等,设置为主键的字段取值不能重复(唯一),也不能为空(非空),否则无法唯一标识一条记录。
主键可以是单个字段,也可以是多个字段组合。对于单字段主键的添加可使用表级约束,也可以使用列级约束;而对于多字段主键的添加只能使用表级约束。
2.非空约束
非空约束(NOT NULL,缩写NK)规定了一张表中指定的某个字段的值不能为空(NULL)。设置了非空约束的字段,在插入的数据为NULL时,数据库会提示错误,导致数据无法插入。
无论是单个字段还是多个字段非空约束的添加只能使用列级约束(非空约束无表级约束)
为已存在表中的字段添加非空约束
alter table student8 modify stu_sex varchar(1) not null;
使用ALTER TABLE语句删除非空约束
alter table student8 modify stu_sex varchar(1) null;
- 唯一约束
唯一约束(UNIQUE,缩写UK)比较简单,它规定了一张表中指定的某个字段的值不能重复,即这一字段的每个值都是唯一的。如果想要某个字段的值不重复,那么就可以为该字段添加为唯一约束。
无论单个字段还是多个字段唯一约束的添加均可使用列级约束和表级约束
- 检查约束
检查约束(CHECK)用来限制某个字段的取值范围,可以定义为列级约束,也可以定义为表级约束。MySQL8开始支持检查约束。
- 默认值约束
默认值约束(DEFAULT)用来规定字段的默认值。如果某个被设置为DEFAULT约束的字段没插入具体值,那么该字段的值将会被默认值填充。
默认值约束的设置与非空约束一样,也只能使用列级约束。
- 字段值自动增加约束
自增约束(AUTO_INCREMENT)可以使表中某个字段的值自动增加。一张表中只能有一个自增长字段,并且该字段必须定义了约束(该约束可以是主键约束、唯一约束以及外键约束),如果自增字段没有定义约束,数据库则会提示“Incorrect table definition; there can be only one auto column and it must be defined as a key”错误。
由于自增约束会自动生成唯一的ID,所以自增约束通常会配合主键使用,并且只适用于整数类型。一般情况下,设置为自增约束字段的值会从1开始,每增加一条记录,该字段的值加1。
为已存在表中的字段添加自增约束
/*创建表student11*/
create table student11 (
stu_id int(10) primary key,
stu_name varchar(3),
stu_sex varchar (1)
);
/*为student11表中的主键字段添加自增约束*/
alter table student11 modify stu_id int(10) auto_increment
使用ALTER TABLE语句删除自增约束
alter table studen11 modify stu_id int(10);
create table t_student(
-------------------行约束--------------------
sno int(6) auto_increment,
sname varchar(5) not null,
sex char(1) default '男',
age int(3),
enterdate date,
classname varchar(10),
email varchar(15),
------------------表约束-----------------------
constraint pk_stu primary key (sno), -- pk_stu 主键约束的名字
constraint ck_stu_sex check (sex = '男' || sex = '女'),
constraint ck_stu_age check (age >= 18 and age <= 50),
constraint uq_stu_email unique (email)
);
------------------------- 在创建表以后添加约束------------
alter table t_student add constraint pk_stu primary key (sno) ; -- 主键约束
alter table t_student modify sno int(6) auto_increment; -- 修改自增条件
alter table t_student add constraint ck_stu_sex check (sex = '男' || sex = '女');
alter table t_student add constraint ck_stu_age check (age >= 18 and age <= 50);
alter table t_student add constraint uq_stu_email unique (email);
外键约束
【1】什么是外键约束?
外键约束(FOREIGN KEY,缩写FK)是用来实现数据库表的参照完整性的。外键约束可以使两张表紧密的结合起来,特别是针对修改或者删除的级联操作时,会保证数据的完整性。
外键是指表中某个字段的值依赖于另一张表中某个字段的值,而被依赖的字段必须具有主键约束或者唯一约束。被依赖的表我们通常称之为父表或者主表,设置外键约束的表称为子表或者从表。
举个例子:如果想要表示学生和班级的关系,首先要有学生表和班级表两张表,然后学生表中有个字段为stu_clazz(该字段表示学生所在的班级),而该字段的取值范围由班级表中的主键cla_no字段(该字段表示班级编号)的取值决定。那么班级表为主表,学生表为从表,且stu_clazz字段是学生表的外键。通过stu_clazz字段就建立了学生表和班级表的关系。
主表(父表):班级表 - 班级编号 - 主键
从表(子表):学生表 - 班级编号 - 外键
create table t_class(
cno int(4) primary key auto_increment,
cname varchar(10) not null,
room char(4)
)
-- 添加班级数据:
insert into t_class values (null,'java001','r803');
insert into t_class values (null,'java002','r416');
insert into t_class values (null,'大数据001','r103');
create table t_student(
sno int(6) primary key auto_increment,
sname varchar(5) not null,
classno int(4),-- 取值参考t_class表中的cno字段,不要求字段名字完全重复,但是类型长度定义 尽量要求相同。
constraint fk_stu_classno -- 给这个外键起名字,可不写
foreign key (classno) references t_class (cno)
);
create table t_student(
sno int(6) primary key auto_increment,
sname varchar(5) not null,
classno int(4)
);
-- 在创建表以后添加外键约束:
alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno)
注意 :
删除时先删从表,后删主表****(有外键的能删除,被关联的不能删)
加入外键策略删除主表
策略1:no action 不允许操作
通过操作sql来完成:
先把班级2的学生对应的班级 改为null
update t_student set classno = null where classno = 2;
-- 然后再删除班级2:
delete from t_class where cno = 2;
策略2:cascade 级联操作:操作主表的时候影响从表的外键信息:
先删除之前的外键约束:
alter table t_student drop foreign key fk_stu_classno;
重新添加外键约束:
alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno) on update cascade on delete cascade;
试试更新:
update t_class set cno = 5 where cno = 3;
试试删除:
delete from t_class where cno = 5;
-- 策略3:set null 置空操作:
先删除之前的外键约束:
alter table t_student drop foreign key fk_stu_classno;
重新添加外键约束:
alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno) on update set null on delete set null;
试试更新:
update t_class set cno = 8 where cno = 1;
-- 注意:
\1. 策略2 级联操作 和 策略2 的 删除操作 可以混着使用:
alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno) on update cascade on delete set null ;
2.应用场合:
(1)朋友圈删除,点赞。留言都删除 -- 级联操作
(2)解散班级,对应的学生 置为班级为null就可以了,-- set null
- 补充
【1】sql展示:
-- 创建表:
create table t_student(
sno int(6) primary key auto_increment,
sname varchar(5) not null,
sex char(1) default '男' check(sex='男' || sex='女'),
age int(3) check(age>=18 and age<=50),
enterdate date,
classname varchar(10),
email varchar(15) unique
);
-- 添加数据:
insert into t_student values (null,'张三','男',21,'2023-9-1','java01班','zs@126.com');
insert into t_student values (null,'李四','男',21,'2023-9-1','java01班','ls@126.com');
insert into t_student values (null,'露露','男',21,'2023-9-1','java01班','ll@126.com');
-- 查看学生表:
select * from t_student;
-- 添加一张表:快速添加:结构和数据跟t_student 都是一致的
create table t_student2
as
select * from t_student;
select * from t_student2;
-- 快速添加,结构跟t_student一致,数据没有:
create table t_student3
as
select * from t_student where 1=2;
select * from t_student3;
-- 快速添加:只要部分列,部分数据:
create table t_student4
as
select sno,sname,age from t_student where sno = 2;
select * from t_student4;
-- 删除数据操作 :清空数据
delete from t_student;
truncate table t_student;
【2】delete和truncate的区别:
从最终的结果来看,虽然使用TRUNCATE操作和使用DELETE操作都可以删除表中的全部记录,但是两者还是有很多区别的,其区别主要体现在以下几个方面:
(1)DELETE为数据操作语言DML;TRUNCATE为数据定义语言DDL。
(2) DELETE操作是将表中所有记录一条一条删除直到删除完;TRUNCATE操作则是保留了表的结构,重新创建了这个表,所有的状态都相当于新表。因此,TRUNCATE操作的效率更高。
(3)DELETE操作可以回滚;TRUNCATE操作会导致隐式提交,因此不能回滚(在第十章中会讲解事务的提交和回滚)。
(4)DELETE操作执行成功后会返回已删除的行数(如删除4行记录,则会显示“Affected rows:4”);截断操作不会返回已删除的行量,结果通常是“Affected rows:0”。DELETE操作删除表中记录后,再次向表中添加新记录时,对于设置有自增约束字段的值会从删除前表中该字段的最大值加1开始自增;TRUNCATE操作则会重新从1开始自增。
单表查询
准备几张表:dept(部门表),emp(员工表),salgrade(薪资等级表),bonus(奖金表)
create table DEPT(
DEPTNO int(2) not null,
DNAME VARCHAR(14),
LOC VARCHAR(13)
);
alter table DEPT
add constraint PK_DEPT primary key (DEPTNO);
create table EMP
(
EMPNO int(4) primary key,
ENAME VARCHAR(10),
JOB VARCHAR(9),
MGR int(4),
HIREDATE DATE,
SAL double(7,2),
COMM double(7,2),
DEPTNO int(2)
);
alter table EMP
add constraint FK_DEPTNO foreign key (DEPTNO)
references DEPT (DEPTNO);
create table SALGRADE
(
GRADE int primary key,
LOSAL double(7,2),
HISAL double(7,2)
);
create table BONUS
(
ENAME VARCHAR(10),
JOB VARCHAR(9),
SAL double(7,2),
COMM double(7,2)
);
insert into DEPT (DEPTNO, DNAME, LOC)
values (10, 'ACCOUNTING', 'NEW YORK');
insert into DEPT (DEPTNO, DNAME, LOC)
values (20, 'RESEARCH', 'DALLAS');
insert into DEPT (DEPTNO, DNAME, LOC)
values (30, 'SALES', 'CHICAGO');
insert into DEPT (DEPTNO, DNAME, LOC)
values (40, 'OPERATIONS', 'BOSTON');
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500, 0, 30);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20);
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);
insert into SALGRADE (GRADE, LOSAL, HISAL)
values (1, 700, 1200);
insert into SALGRADE (GRADE, LOSAL, HISAL)
values (2, 1201, 1400);
insert into SALGRADE (GRADE, LOSAL, HISAL)
values (3, 1401, 2000);
insert into SALGRADE (GRADE, LOSAL, HISAL)
values (4, 2001, 3000);
insert into SALGRADE (GRADE, LOSAL, HISAL)
values (5, 3001, 9999);
-- 查看表:
select * from dept;
-- 部门表:dept:department 部分 ,loc - location 位置
select * from emp;
-- 员工表:emp:employee 员工 ,mgr :manager上级领导编号,hiredate 入职日期 firedate 解雇日期 ,common:补助
-- deptno 外键 参考 dept - deptno字段
-- mgr 外键 参考 自身表emp - empno 产生了自关联
select * from salgrade;
-- losal - lowsal
-- hisal - highsal
select * from bonus;
最简单的SQL查询
-- 对emp表查询:
select * from emp; -- *代表所有
数据-- 显示部分列:
select empno,ename,sal from emp;
-- 显示部分行:where子句
select * from emp where sal > 2000;
-- 显示部分列,部分行:
select empno,ename,job,mgr from emp where sal > 2000;
-- 起别名:
select empno 员工编号,ename 姓名,sal 工资 from emp; -- as 省略,''或者""省略了
-- as alias 别名
select empno as 员工编号,ename as 姓名,sal as 工资 from emp;
select empno as '员工编号',ename as "姓名",sal as 工资 from emp;
-- > 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server
version for the right syntax to use near '编号,ename as "姓 名",sal as 工资 from emp' at line 1
-- 错误原因:在别名中有特殊符号的时候,''或者""不可以省略不写
select empno as 员工 编号,ename as "姓 名",sal as 工资 from emp;
-- 算术运算符:
select empno,ename,sal,sal+1000 as '涨薪后',deptno from emp where sal < 2500;
select empno,ename,sal,comm,sal+comm from emp; -- ???后面再说
-- 去重操作:
select job from emp;
select distinct job from emp;
select job,deptno from emp;
select distinct job,deptno from emp; -- 对后面的所有列组合 去重 ,而不是单独的某一列去重
-- 排序:
select * from emp order by sal; -- 默认情况下是按照升序排列的
select * from emp order by sal asc; -- asc 升序,可以默认不写
select * from emp order by sal desc; -- desc 降序
select * from emp order by sal asc ,deptno desc; -- 在工资升序的情况下,deptno按照降序排列
where子局
-- 查看emp表:
select * from emp;
-- where子句:将过滤条件放在where子句的后面,可以筛选/过滤出我们想要的符合条件的数据:
-- where 子句 + 关系运算符
select * from emp where deptno = 10;
select * from emp where deptno > 10;
select * from emp where deptno >= 10;
select * from emp where deptno < 10;
select * from emp where deptno <= 10;
select * from emp where deptno <> 10;
select * from emp where deptno != 10;
select * from emp where job = 'CLERK';
select * from emp where job = 'clerk'; -- 默认情况下不区分大小写
select * from emp where binary job = 'clerk'; -- binary区分大小写
select * from emp where hiredate < '1981-12-25';
-- where 子句 + 逻辑运算符:and
select * from emp where sal > 1500 and sal < 3000; -- (1500,3000)
select * from emp where sal > 1500 && sal < 3000;
select * from emp where sal > 1500 and sal < 3000 order by sal;
select * from emp where sal between 1500 and 3000; -- [1500,3000]
-- where 子句 + 逻辑运算符:or
select * from emp where deptno = 10 or deptno = 20;
select * from emp where deptno = 10 || deptno = 20;
select * from emp where deptno in (10,20);
select * from emp where job in ('MANAGER','CLERK','ANALYST');
-- where子句 + 模糊查询:
-- 查询名字中带A的员工 -- %代表任意多个字符 0,1,2,.....
select * from emp where ename like '%A%' ;
-- -任意一个字符
select * from emp where ename like '__A%' ;
-- 关于null的判断:
select * from emp where comm is null;
select * from emp where comm is not null;
-- 小括号的使用 :因为不同的运算符的优先级别不同,加括号为了可读性
select * from emp where job = 'SALESMAN' or job = 'CLERK' and sal >=1500; -- 先and再or and > or
select * from emp where job = 'SALESMAN' or (job = 'CLERK' and sal >=1500);
select * from emp where (job = 'SALESMAN' or job = 'CLERK') and sal >=1500;
函数
1.字符串函数 (String StringBuilder)
函数 | 描述 |
---|---|
CONCAT(str1, str2, ···, strn) | 将str1、str2···strn拼接成一个新的字符串 |
INSERT(str, index, n, newstr) | 将字符串str从第index位置开始的n个字符替换成字符串newstr |
LENGTH(str) | 将字符串str从第index位置开始的n个字符替换成字符串newstr |
LOWER(str) | 将字符串str中的每个字符转换为小写 |
UPPER(str) | 将字符串str中的每个字符转换为大写 |
LEFT(str, n) | 获取字符串str最左边的n个字符 |
RIGHT(str, n) | 获取字符串str最右边的n个字符 |
LPAD(str, n, pad) | 获取字符串str最右边的n个字符 |
RPAD(str, n, pad) | 使用字符串pad在str的最右边进行填充,直到长度为n个字符为止 |
LTRIM(str) | 去除字符串str左侧的空格 |
RTRIM(str) | 去除字符串str右侧的空格 |
TRIM(str) | 去除字符串str左右两侧的空格 |
REPLACE(str,oldstr,newstr) | 用字符串newstr替换字符串str中所有的子字符串oldstr |
REPLACE(str,oldstr,newstr) | 将字符串str中的字符逆序 |
STRCMP(str1, str2) | 比较字符串str1和str2的大小 |
SUBSTRING(str,index,n) | 获取从字符串str的index位置开始的n个字符 |
2. 数值函数 (Math)
函数 | 描述 |
---|---|
ABS(num) | 返回num的绝对值 |
CEIL(num) | 返回大于num的最小整数(向上取整) |
FLOOR(num) | 返回小于num的最大整数(向下取整) |
MOD(num1, num2) | 返回num1/num2的余数(取模) |
PI() | 返回圆周率的值 |
POW(num,n)/POWER(num, n) | 返回num的n次方 |
POW(num,n)/POWER(num, n) | 返回x四舍五入后的值,该值保留到小数点后n位 |
ROUND(num, n) | 返回x四舍五入后的值,该值保留到小数点后n位 |
TRUNCATE(num, n) | 返回num被舍去至小数点后n位的值 |
3. 日期与时间函数
函数 | 描述 |
---|---|
CURDATE() | 返回当前日期 |
CURTIME() | 返回当前时间 |
NOW() | 返回当前日期和时间 |
SYSDATE() | 返回该函数执行时的日期和时间 |
DAYOFYEAR(date) | 返回日期date为一年中的第几天 |
WEEK(date)/WEEKOFYEAR(date) | 返回日期date为一年中的第几周 |
DATE_FORMAT(date, format) | 返回按字符串format格式化后的日期date |
DATE_ADD(date, INTERVAL expr unit)/ADDDATE(date, INTERVAL expr unit) | 返回date加上一个时间间隔后的新时间值 |
DATE_SUB(date, INTERVAL expr unit)/SUBDATE(date, INTERVAL expr unit) | 返回date减去一个时间间隔后的新时间值 |
DATEDIFF(date1, date2) | 返回起始日期date1与结束日期date2之间的间隔天数 |
-- 单行函数包含:
-- 1.字符串函数
select ename,length(ename),substring(ename,2,3) from emp;
-- substring字符串截取,2:从字符下标为2开始,3:截取长度3 (下标从1开始)
-- 2.数值函数
select abs(-5),ceil(5.3),floor(5.9),round(3.14) from dual; -- dual实际就是一个伪表
select abs(-5) 绝对值,ceil(5.3) 向上取整,floor(5.9) 向下取整,round(3.14) 四舍五入; -- 如果没有where条件的话,from dual可以省略不写
select ceil(sal) from emp;
select 10/3,10%3,mod(10,3) ;
-- 3.日期与时间函数
select * from emp;
select curdate(),curtime() ; -- curdate()年月日 curtime()时分秒
select now(),sysdate(),sleep(3),now(),sysdate() from dual; -- now(),sysdate() 年月日时分秒
insert into emp values (9999,'lili','SALASMAN',7698,now(),1000,null,30);
-- now()可以表示年月日时分秒,但是插入数据的时候还是要参照表的结构的
desc emp;
4. 流程函数( IF SWITCH)
间隔函数 | 描述 |
---|---|
IF(condition, t, f) | 如果条件condition为真,则返回t,否则返回f |
IFNULL(value1, value2) | 如果value1等于value2,则返回null,否则返回value1 |
NULLIF(value1, value2) | 如果value1等于value2,则返回null,否则返回value1 |
CASE value WHEN [value1] THEN result1 [WHEN [value2] THEN result2 ...] [ELSE result] END | 如果value等于value1,则返回result1,···,否则返回result |
CASE WHEN [condition1] THEN result1 [WHEN [condition2] THEN result2 ...] [ELSE result] END | 如果条件condition1为真,则返回result1,···,否则返回result |
-- if相关
select empno,ename,sal,if(sal>=2500,'高薪','底薪') as '薪资等级' from emp; -- if-else 双分支结构
select empno,ename,sal,comm,sal+ifnull(comm,0) from emp; -- 如果comm是null,那么取值为0 -- 单分支
select nullif(1,1),nullif(1,2) from dual; -- 如果value1等于value2,则返回null,否则返回value1
-- case相关:
-- case等值判断
select empno,ename,job,
case job
when 'CLERK' then '店员'
when 'SALESMAN' then '销售'
when 'MANAGER' then '经理'
else '其他'
end '岗位',
sal from emp;
-- case区间判断:
select empno,ename,sal,
case
when sal<=1000 then 'A'
when sal<=2000 then 'B'
when sal<=3000 then 'C'
else 'D'
end '工资等级',
deptno from emp;
from emp;
5.JSON函数
函数 | 描述 |
---|---|
JSON_APPEND() | 在JSON文档中追加数据 |
JSON_INSERT () | 在JSON文档中插入数据 |
JSON_REPLACE () | 替换JSON文档中的数据 |
JSON_REMOVE () | 从JSON文档的指定位置移除数据 |
JSON_CONTAINS() | 判断JSON文档中是否包含某个数据 |
JSON_SEARCH() | 查找JSON文档中给定字符串的路径 |
6.其他函数
函数 | 描述 |
---|---|
DATABASE() | 返回当前数据库名 |
VERSION() | 返回IP地址的数字表示 |
USER() | 返回IP地址的数字表示 |
INET_ATON(IP) | 返回IP地址的数字表示 |
INET_ATON(IP) | 返回数字代表的IP地址 |
PASSWORD(str) | 实现对字符串str的加密操作 |
PASSWORD(str) | 实现对数字num的格式化操作,保留n位小数 |
CONVERT(data, type) | 实现将数据data转换成type类型的操作 |
-- 6.其他函数
select database(),user(),version() from dual;
多行函数
对一组数据进行运算,针对一组数据(多行记录)只返回一个结果,也称分组函数
函数 | 描述 |
---|---|
COUNT() | 统计表中记录的数目 |
SUM() | 计算指定字段值的总和 |
AVG() | 计算指定字段值的平均值 |
MAX() | 统计指定字段值的最大值 |
MIN() | 统计指定字段值的最小值 |
group_by分组
【1】group by : 用来进行分组
【2】sql展示:
select * from emp;
-- 统计各个部门的平均工资
select deptno,avg(sal) from emp; -- 字段和多行函数不可以同时使用
select deptno,avg(sal) from emp group by deptno; -- 字段和多行函数不可以同时使用,除非这个字段属于分组
select deptno,avg(sal) from emp group by deptno order by deptno desc;
-- 统计各个岗位的平均工资
select job,avg(sal) from emp group by job;
select job,lower(job),avg(sal) from emp group by job;
having分组后筛选
-- 统计各个部门的平均工资 ,只显示平均工资2000以上的 - 分组以后进行二次筛选 having
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2000;
select deptno,avg(sal) 平均工资 from emp group by deptno having 平均工资 > 2000;
select deptno,avg(sal) 平均工资 from emp group by deptno having 平均工资 > 2000 order by deptno desc;
-- 统计各个岗位的平均工资,除了MANAGER
-- 方法1:
select job,avg(sal) from emp where job != 'MANAGER' group by job;
-- 方法2:
select job,avg(sal) from emp group by job having job != 'MANAGER' ;
-- where在分组前进行过滤的,having在分组后进行后滤。
单表查询总结
【1】select语句总结
select column, group_function(column)
from table
[where condition]
[group by group_by_expression]
[having group_condition]
[order by column];
注意:顺序固定,不可以改变顺序
【2】select语句的执行顺序
from--where -- group by– select - having- order by
【3】单表查询练习:
-- 单表查询练习:
-- 列出工资最小值小于2000的职位
select job,min(sal)
from emp
group by job
having min(sal) < 2000 ;
-- 列出平均工资大于1200元的部门和工作搭配组合
select deptno,job,avg(sal)
from emp
group by deptno,job
having avg(sal) > 1200
order by deptno;
-- 统计[人数小于4的]部门的平均工资。
select deptno,count(1),avg(sal)
from emp
group by deptno
having count(1) < 4
-- 统计各部门的最高工资,排除最高工资小于3000的部门。
select deptno,max(sal)
from emp
group by deptno
having max(sal) < 3000;
多表查询
在MySQL中可以使⽤join实现多表的联合查询——连接查询,join按照其功能不同分为
三个操作:
- inner join 内连接
- left join 左连接
- right join 右连接
数据准备
#1.创建班级信息表 和 学⽣信息表
create table classes(
class_id int primary key auto_increment,
class_name varchar(40) not null unique,
class_remark varchar(200) );
create table students(
stu_num char(8) primary key,
stu_name varchar(20) not null,
stu_gender char(2) not null,
stu_age int not null,
cid int,
constraint FK_STUDENTS_CLASSES foreign key(cid) references
classes(class_id) ON UPDATE CASCADE ON DELETE CASCADE
);
#2.添加数据
# Java2104 包含三个学⽣信息
insert into classes(class_name,class_remark) values('Java2104','...');
# Java2105 包含两个学⽣信息
insert into classes(class_name,class_remark) values('Java2105','...');
# 以下两个班级在学⽣表中没有对应的学⽣信息
insert into classes(class_name,class_remark) values('Java2106','...');
insert into classes(class_name,class_remark) values('Python2105','...');
# 以下三个学⽣信息 属于 class_id=1 的班级 (Java2104)
insert into students(stu_num,stu_name,stu_gender,stu_age,cid)
values('20210101','张三','男',20,1);
insert into students(stu_num,stu_name,stu_gender,stu_age,cid)
values('20210102','李四','⼥',20,1);
insert into students(stu_num,stu_name,stu_gender,stu_age,cid)
values('20210103','王五','男',20,1);
# 以下三个学⽣信息 属于 class_id=2 的班级 (Java2105)
insert into students(stu_num,stu_name,stu_gender,stu_age,cid)
values('20210104','赵柳','⼥',20,2);
insert into students(stu_num,stu_name,stu_gender,stu_age,cid)
values('20210105','孙七','男',20,2);
# ⼩红和⼩明没有设置班级信息
insert into students(stu_num,stu_name,stu_gender,stu_age)
values('20210106','⼩红','⼥',20);
insert into students(stu_num,stu_name,stu_gender,stu_age)
values('20210107','⼩明','男',20);
1.内连接 INNER JOIN
select ... from tableName1 inner join tableName2 ON 匹配条件 [where 条件];
1.1笛卡尔积
笛卡尔积(
- A集合&B集合):使⽤A中的每个记录⼀次关联B中每个记录,笛卡尔积的总数=A总数*B总数
- 如果直接执⾏ select ... from tableName1 inner join tableName2; 会获取两种数据表中的数据集合的笛卡尔积(依次使⽤tableName1 表中的每⼀条记录 去 匹配tableName2的每条数据)
1.2 内连接条件
两张表时⽤inner join连接查询之后⽣产的笛卡尔积数据中很多数据都是⽆意义的,我们如何消除⽆意义的数据呢? —— 添加两张进⾏连接查询时的条件
- 使⽤ on 设置两张表连接查询的匹配条件
-- 使⽤where设置过滤条件:先⽣成笛卡尔积再从笛卡尔积中过滤数据(效率很低)
select * from students INNER JOIN classes where students.cid =
classes.class_id;
-- 使⽤ON设置连接查询条件:先判断连接条件是否成⽴,如果成⽴两张表的数据进⾏组合⽣成⼀
条结果记录
select * from students INNER JOIN classes ON students.cid =
classes.class_id;
-
结果:只获取两种表中匹配条件成⽴的数据,任何⼀张表在另⼀种表如果没有找到对应
匹配则不会出现在查询结果中(例如:⼩红和⼩明没有对应的班级信息,Java2106和
Python2106没有对应的学⽣)。
2.左连接 LEFT JOIN
需求:请查询出所有的学⽣信息,如果学⽣有对应的班级信息,则将对应的班级信息也查询出来
左连接:显示左表中的所有数据,如果在有右表中存在与左表记录满⾜匹配条件的数据,则进⾏匹配;如果右表中不存在匹配数据,则显示为Null
# 语法
select * from leftTabel LEFT JOIN rightTable ON 匹配条件 [where 条件];
-- 左连接 : 显示左表中的所有记录
select * from students LEFT JOIN classes ON students.cid =
classes.class_id;
3 右连接 RIGHT JOIN
-- 右连接 :显示右表中的所有记录
select * from students RIGHT JOIN classes ON students.cid =
classes.class_id;
4 数据表别名
如果在连接查询的多张表中存在相同名字的字段,我们可以使⽤ 表名.字段名 来进⾏区分,如果表名太⻓则不便于SQL语句的编写,我们可以使⽤数据表别名
select s.*,c.class_name
from students s
INNER JOIN classes c
ON s.cid = c.class_id;
5 ⼦查询/嵌套查询
⼦查询 — 先进⾏⼀次查询,第⼀次查询的结果作为第⼆次查询的源/条件(第⼆次查询是基于第⼀次的查询结果来进⾏的)
5.1 ⼦查询返回单个值-单⾏单列
案例1: 查询班级名称为'Java2104'班级中的学⽣信息 (只知道班级名称,⽽不知道班级ID)
传统的⽅式:
⼦查询:
8.5.2 ⼦查询返回多个值-多⾏单列
案例2: 查询所有Java班级中的学⽣信息
- 传统的⽅式:
-- a.查询Java2104班的班级编号
select class_id from classes where class_name='Java2104';
-- b.查询此班级编号下的学⽣信息
select * from students where cid = 1;
- ⼦查询:
-- 如果⼦查询返回的结果是⼀个值(单列单⾏),条件可以直接使⽤关系运算符(= !=
....)
select * from students where cid = (select class_id from classes where
class_name='Java2105');
5.2 ⼦查询返回多个值-多⾏单列
案例2: 查询所有Java班级中的学⽣信息
- 传统的⽅式:
-- a.查询所有Java班的班级编号
select class_id from classes where class_name LIKE 'Java%';
+--------------+
| class_id |
+--------------+
| 1 | | 2 | | 3 |
+--------------+
-- b.查询这些班级编号中的学⽣信息(union 将多个查询语句的结果整合在⼀起)
select * from students where cid=1
UNION
select * from students where cid=2
UNION
select * from students where cid=3;
- ⼦查询
-- 如果⼦查询返回的结果是多个值(单列多⾏),条件使⽤IN / NOT IN
select * from students where cid IN (select class_id from classes where
class_name LIKE 'Java%');
5.3 ⼦查询返回多个值-多⾏多列
案例3: 查询cid=1的班级中性别为男的学⽣信息
-- 多条件查询:
select * from students where cid=1 and stu_gender='男';
-- ⼦查询:先查询cid=1班级中的所有学⽣信息,将这些信息作为⼀个整体虚拟表(多⾏多列)
-- 再基于这个虚拟表查询性别为男的学⽣信息(‘虚拟表’需要别名)
select * from (select * from students where cid=1) t where
t.stu_gender='男';
数据库对象
事务
事务(Transaction)是用来维护数据库完整性的,它能够保证一系列的MySQL操作要么全部执行,要么全不执行。
例子1:
举一个例子来进行说明,例如转账操作:A账户要转账给B账户,那么A账户上减少的钱数和B账户上增加的钱数必须一致,也就是说A账户的转出操作和B账户的转入操作要么全部执行,要么全不执行;如果其中一个操作出现异常而没有执行的话,就会导致账户A和账户B的转入转出金额不一致的情况,为而事实上这种情况是不允许发生的,所以为了防止这种情况的发生,需要使用事务处理。
例子2:
在淘宝购物下订单的时候,商家库存要减少,订单增加记录,付款我的账号少100元...操作要么全部执行,要么全不执行
【1】事务的概念
事务(Transaction)指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由DBMS(数据库管理系统)中的事务管理子系统负责事务的处理。
目前常用的存储引擎有InnoDB(MySQL5.5以后默认的存储引擎)和MyISAM(MySQL5.5之前默认的存储引擎),其中InnoDB支持事务处理机制,而MyISAM不支持。
【2】事务的特性
事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。
但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性简称为ACID特性。
- 原子性
原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。
使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性。
-
一致性
一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。
例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。 -
隔离性
隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。
例如:在转账时,只有当A账户中的转出和B账户中转入操作都执行成功后才能看到A账户中的金额减少以及B账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。
- 持久性
持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。
【3】sql展示:使用事务保证转账安全
-- 创建账户表:
create table account(
id int primary key auto_increment,
uname varchar(10) not null,
balance double
);
-- 查看账户表:
select * from account;
-- 在表中插入数据:
insert into account values (null,'丽丽',2000),(null,'小刚',2000);
-- 丽丽给小刚 转200元:
update account set balance = balance - 200 where id = 1;
update account set balance = balance + 200 where id = 2;
-- 默认一个DML语句是一个事务,所以上面的操作执行了2个事务。
update account set balance = balance - 200 where id = 1;
update account set balance = balance2 + 200 where id = 2;
-- 必须让上面的两个操作控制在一个事务中:
-- 手动开启事务:
start transaction;
update account set balance = balance - 200 where id = 1;
update account set balance = balance + 200 where id = 2;
-- 手动回滚:刚才执行的操作全部取消:
rollback;
-- 手动提交:
commit;
-- 在回滚和提交之前,数据库中的数据都是操作的缓存中的数据,而不是数据库的真实数据
事务并发问题
脏读(Dirty read)
当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
不可重复读
(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读
(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读
事务隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
事务的隔离级别从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | √ | √ | √ |
READ COMMITTED | × | √ | √ |
REPEATABLE READ | × | × | √ |
SERIALIZABLE | × | × | × |
PS:√ 代表会出现问题 , ×代表不会出现问题 = 解决问题
-- 查看默认的事务隔离级别 MySQL默认的是repeatable read
select @@transaction_isolation;
-- 设置事务的隔离级别 (设置当前会话的隔离级别)
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;
start transaction ;
select * from account where id = 1;
mysql事务(内存):
-
原子(本次事务不能参杂其他事务,独立,唯一)
-
持久(内存写到硬盘)
-
一致
-
隔离(在内存里保证数据不被别人读取)
隔离
- 读未提交:---->脏读
2.读已提交:---->解决了脏读 出现不可重复读 oracle默认
- 可重复读 ---> 解决了脏读 和 不可重复读 mysql默认
读之前读的数据
- 序列化读:--->锁全表,我读时别不能读
视图
【1】视图的概念
视图(view)是一个从单张或多张基础数据表或其他视图中构建出来的虚拟表。同基础表一样,视图中也包含了一系列带有名称的列和行数据,但是数据库中只是存放视图的定义,也就是动态检索数据的查询语句,而并不存放视图中的数据,这些数据依旧存放于构建视图的基础表中,只有当用户使用视图时才去数据库请求相对应的数据,即视图中的数据是在引用视图时动态生成的。因此视图中的数据依赖于构建视图的基础表,如果基本表中的数据发生了变化,视图中相应的数据也会跟着改变。
PS:视图本质上就是:一个查询语句,是一个虚拟的表,不存在的表,你查看视图,其实就是查看视图对应的sql语句
【2】视图的好处
简化用户操作:视图可以使用户将注意力集中在所关心地数据上,而不需要关心数据表的结构、与其他表的关联条件以及查询条件等。
对机密数据提供安全保护:有了视图,就可以在设计数据库应用系统时,对不同的用户定义不同的视图,避免机密数据(如,敏感字段“salary”)出现在不应该看到这些数据的用户视图上。这样视图就自动提供了对机密数据的安全保护功能
【3】SQL展示:
-- 创建/替换单表视图:
create or replace view myview01
as
select empno,ename,job,deptno
from emp
where deptno = 20
with check option;
-- 查看视图:
select * from myview01;
-- 在视图中插入数据:
insert into myview01 (empno,ename,job,deptno) values (9999,'lili','CLERK',20);
insert into myview01 (empno,ename,job,deptno) values (8888,'nana','CLERK',30);
insert into myview01 (empno,ename,job,deptno) values (7777,'feifei','CLERK',30);
-- > 1369 - CHECK OPTION failed 'mytestdb.myview01'
-- 创建/替换多表视图:
create or replace view myview02
as
select e.empno,e.ename,e.sal,d.deptno,d.dname
from emp e
join dept d
on e.deptno = d.deptno
where sal > 2000 ;
select * from myview02;
-- 创建统计视图:
create or replace view myview03
as
select e.deptno,d.dname,avg(sal),min(sal),count(1)
from emp e
join dept d
using(deptno)
group by e.deptno ;
select * from myview03;
-- 创建基于视图的视图:
create or replace view myview04
as
select * from myview03 where deptno = 20;
select * from myview04;
存储过程
【1】什么是存储过程(Stored Procedure)
通过前面章节的学习,我们已经知道SQL是一种非常便利的语言。从数据库抽取数据,或者对特定的数据集中更新时,都能通过简洁直观的代码实现。
但是这个所谓的“简洁”也是有限制,SQL基本是一个命令实现一个处理,是所谓的非程序语言。
在不能编写流程的情况下,所有的处理只能通过一个个命令来实现。当然,通过使用连接及子查询,即使使用SQL的单一命令也能实现一些高级的处理,但是,其局限性是显而易见的。例如,在SQL中就很难实现针对不同条件进行不同的处理以及循环等功能。
这个时候就出现了存储过程这个概念,简单地说,存储过程就是数据库中保存(Stored)的一系列SQL命令(Procedure)的集合。也可以将其看作相互之间有关系的SQL命令组织在一起形成的一个小程序。
【2】存储过程的优点
-
提高执行性能。存储过程执行效率之所高,在于普通的SQL语句,每次都会对语法分析,编译,执行,而存储过程只是在第一次执行语法分析,编译,执行,以后都是对结果进行调用。
-
可减轻网络负担。使用存储过程,复杂的数据库操作也可以在数据库服务器中完成。只需要从客户端(或应用程序)传递给数据库必要的参数就行,比起需要多次传递SQL命令本身,这大大减轻了网络负担。
-
可将数据库的处理黑匣子化。应用程序中完全不用考虑存储过程的内部详细处理,只需要知道调用哪个存储过程就可以了
【3】图解:
【4】展示存储过程:
-- 定义一个没有返回值 存储过程
-- 实现:模糊查询操作:
select * from emp where ename like '%A%';
create procedure mypro01(name varchar(10))
begin
if name is null or name = "" then
select * from emp;
else
select * from emp where ename like concat('%',name,'%');
end if;
end;
-- 删除存储过程:
drop procedure mypro01;
-- 调用存储过程:
call mypro01(null);
call mypro01('R');
-- 定义一个 有返回值的存储过程:
-- 实现:模糊查询操作:
-- in 参数前面的in可以省略不写
-- found_rows()mysql中定义的一个函数,作用返回查询结果的条数
create procedure mypro02(in name varchar(10),out num int(3))
begin
if name is null or name = "" then
select * from emp;
else
select * from emp where ename like concat('%',name,'%');
end if;
select found_rows() into num;
end;
-- -- 调用存储过程:
call mypro02(null,@num);
select @num;
call mypro02('R',@aaa);
select @aaa;