数据库基础知识整理

数据库注入漏洞

主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击者可以通过合法的输入点提交一些精心构造的语句,从而欺骗后台数据库对其进行执行,导致数据库信息泄露的一种漏洞

数据库知识(本文以mysql为例)

概念

  1. 数据库:长期存储在计算机内、有组织、可共享的大量的数据的集合。数据库中的数据按 照一定的数据模型组织、描述和存储,具有较小的冗余度、较高的数据独立性和易扩展性,并可为各种用户共享。特点:永久存储、有组织、可共享。
  2. 数据库管理系统(DBMS):位于用户与操作系统之间的一层数据管理软件。主要功能:数据定义功能(DDL);数据组织、存储和管理;数据操纵功能(DML);数据库的事务管理和运行管理;数据库的建立和维护功能;其他功能。
  3. 数据库系统(DBS):由数据库、数据库管理系统(及其开发工具)、应用系统、数据库管理员构成。
  4. 数据管理技术三个阶段:人工管理、文件系统、数据库系统。
  5. 数据模型:概念模型(又叫信息模型);逻辑模型、物理模型
  6. 数据模型的组成要素:数据结构、数据操纵和数据的完整性约束条件。
  7. 概念模型:用于信息世界的建模,是现实世界到信息世界的第一层抽象,是数据库设计人员进行数据库设计的有力工具也是数据库设计人员与用户之间进行交流的语言。
  8. 关系的完整性约束条件包括三大类:实体完整性、参照完整性、和用户自定义完整性。
  9. 数据库系统的三级模式结构:模式、外模式、内模式。
  10. 数据库的二级映像:外模式/模式映像,模式/内模式映像。

MYSQL主流关系数据库(一种专门管理数据的软件)

目前,主流的关系数据库主要分为以下几类:
1.商用数据库,例如:Oracle,SQL Server,DB2等;
2.开源数据库,例如:MySQL,PostgreSQL等;
3.桌面数据库,以微软Access为代表,适合桌面应用程序使用;
4.嵌入式数据库,以Sqlite为代表,适合手机应用和桌面程序。
MySQL【MySQL本身实际上只是一个SQL接口,它的内部还包含了多种数据引擎MySQL接口和数据库引擎的关系就好比某某浏览器和浏览器引擎的关系。对用户而言,切换浏览器引擎不影响浏览器界面,切换MySQL引擎不影响自己写的应用程序使用MySQL的接口。使用MySQL时,不同的表还可以使用不同的数据库引擎】是目前应用最广泛的开源关系数据库。

mysql数据库访问的执行过程?

1.客户端发起请求
2.服务器开启线程相应客户端的请求
3.客户端发起sql查询语句
4.缓存查询,先记录sql语句,查看是否有缓存,有缓存直接在缓存提出,如果缓存没有则进入分析器
5.分析器:进行分析sql命令是否正确,将客户的命令进行切换,将每个词用空格隔开,获得用户要查询的表,内容,用户权限
6.优化器:执行路径的选择,生成执行树。
7.存储引擎:管理存储的文件系统;不同的存储引擎有不同的功能和存储方式。

关系模型

关系数据库是建立在关系模型【由关系数据结构、关系操作集合和关系完整性约束三部分组成】上的。而关系模型本质上就是若干个存储数据的二维表,可以把它们看作很多Excel表。表的每一行称为记录(Record),记录是一个逻辑意义上的数据。表的每一列称为字段(Column),同一个表的每一行记录都拥有相同的若干字段。

关系操作:查询操作和插入、删除、修改操作两大部分。查询操作又可分为选择、投影、连接、除、并、差、交、笛卡尔积等。

主键

对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键主键的选取非常重要:主键不要带有业务含义,记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。而应该使用BIGINT自增(数据库会在插入数据时自动为每一条记录分配一个自增整数)或者GUID类型(使用一种全局唯一的字符串作为主键,类似8f55d96b-8acc-4636-8cb8-76bf8abc2f57)。主键也不应该允许NULL。可以使用多个列作为联合主键,但联合主键并不常用。

外键

可以把数据与另一张表关联起来,这种列称为外键。外键并不是通过列名实现的,而是通过定义外键约束实现的。关系数据库通过外键可以实现一对多、多对多和一对一的关系。外键既可以通过数据库来约束,也可以不设置约束,仅依靠应用程序的逻辑来保证。

索引

索引是关系数据库中对某一列或多个列的值进行预排序的数据结构。通过使用索引,可以让数据库系统不必扫描整个表,而是直接定位到符合条件的记录,这样就大大加快了查询速度。根据业务要求,又具有唯一性约束:即不能出现两条记录存储了同一个身份证号。这个时候,就可以给该列添加一个唯一索引。通过创建唯一索引,可以保证某一列的值具有唯一性。数据库索引对于用户和应用程序来说都是透明的。

修改密码

update user set password=123(要更换的密码)  where user='root'

set password for root@localhost = password('123');       //123为新密码

查询数据

基本查询

使用SELECT查询的基本语句SELECT * FROM <表名>可以查询一个表的所有行和所有列的数据。SELECT是关键字表示将要执行一个查询,*表示“所有列”,FROM表示将要从哪个表查询。查询结果也是一个二维表,它包含列名和每一行的数据。SELECT 100+200 上述查询会直接计算出表达式的结果。虽然SELECT可以用作计算,但它并不是SQL的强项。但是,不带FROM子句的SELECT语句有一个有用的用途,就是用来判断当前到数据库的连接是否有效。许多检测工具会执行一条SELECT 1来测试数据库连接。
在这里插入图片描述

条件查询

SELECT * FROM <表名> WHERE <条件表达式>,通过WHERE条件查询,可以筛选出符合指定条件的记录,而不是整个表的所有记录。
AND条件:

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

NOT条件:

SELECT * FROM students WHERE NOT class_id = 2  #筛选出不是二班的人

OR条件:

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

组合筛选:

SELECT * FROM students WHERE (score < 80 OR score > 90) AND gender = 'M';    # 需要用到小括号

优先级:NOT > AND >OR (加上括号可以改变优先级)
常用条件表达式:
使用=判断相等 # 字符串需要用单引号括起来
使用>判断大于 # 字符串比较根据ASCII码,中文字符比较根据数据库设置
使用>=判断大于或相等
使用<判断小于
使用<=判断小于或相等
使用<>判断不相等
使用LIKE判断相似 name LIKE 'ab%' 【%表示任意字符,例如’ab%‘匹配’ab’,‘abc’,‘abcd’】

投影查询

如果我们只希望返回某些列的数据,而不是所有列的数据,我们可以用SELECT 列1, 列2, 列3 FROM …,让结果集仅包含指定列。这种操作称为投影查询
例如,
① 从students表中返回id、score和name这三列:

`SELECT id, score, name FROM students`

② 以下SELECT语句将列名score重命名为points,而id和name列名保持不变:
语法:使用SELECT 列1, 列2, 列3 FROM …时,还可以给每一列起个别名,这样,结果集的列名就可以与原表的列名不同。它的语法是SELECT 列1 别名1, 列2 别名2, 列3 别名3 FROM …。
例子:

`SELECT id, score points, name FROM students`

③ 投影查询同样可以接WHERE条件,实现复杂的查询:

`SELECT id, score points, name FROM students WHERE gender = 'M'`

排序

我们使用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列有相同的数据,要进一步排序,可以继续添加列名例如,例如使用ORDER BY score DESC, gender表示先按score列倒序,如果有相同分数的,再按gender列排序:

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

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

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

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

分页查询

使用SELECT查询时,如果结果集数据量很大,比如几万行数据,放在一个页面显示的话数据量太大,不如分页显示,每次显示100条。分页功能实际上就是从结果集中“截取”出第M~N条记录,这个查询可以通过LIMIT <M> OFFSET <N>子句实现,LIMIT 3表示的意思是“最多3条记录”(即每页最多显示三条记录,若最后一页只有一条记录,则按实际数量来)。例如:我们先把所有学生按照成绩从高到低进行排序,我们把结果集分页,每页3条记录。要获取第1页的记录,可以使用LIMIT 3 OFFSET 0
分页查询的关键在于,首先要确定每页需要显示的结果数量pageSize(这里是3),然后根据当前页的索引pageIndex(从1开始),确定LIMITOFFSET应该设定的值:

LIMIT总是设定为pageSize;
OFFSET计算公式为pageSize * (pageIndex - 1)。
这样就能正确查询出第N页的记录集。

SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 0;

上述查询LIMIT 3 OFFSET 0表示,对结果集从0号记录开始,最多取3条。注意SQL记录集的索引从0开始;如果要查询第2页,那么我们只需要“跳过”头3条记录,也就是对结果集从3号记录开始查询,把OFFSET设定为3;类似的,查询第3页的时候,OFFSET应该设定为6;查询第4页的时候,OFFSET应该设定为9。
如果原本记录集一共就10条记录,但我们把OFFSET设置为20,会得到什么结果呢?OFFSET超过了查询的最大数量并不会报错,而是得到一个空的结果集。
注意:
OFFSET是可选的,如果只写LIMIT 15,那么相当于LIMIT 15 OFFSET 0
在MySQL中,LIMIT 15 OFFSET 30还可以简写成LIMIT 30, 15
使用LIMIT <M> OFFSET <N>分页时,随着N越来越大,查询效率也会越来越低。

聚合查询

对于统计总数、平均数这类计算,SQL提供了专门的聚合函数,使用聚合函数进行查询,就是聚合查询,它可以快速获得结果。仍然以查询students表一共有多少条记录为例,我们可以使用SQL内置的COUNT()函数查询:【COUNT(*)表示查询所有列的行数,要注意聚合的计算结果虽然是一个数字,但查询的结果仍然是一个二维表,只是这个二维表只有一行一列,并且列名是COUNT(*)

`SELECT COUNT(*) FROM students;

在这里插入图片描述
通常,使用聚合查询时,我们应该给列名设置一个别名,便于处理结果:

`SELECT COUNT(*) num FROM students;`

在这里插入图片描述
COUNT(*)COUNT(id)实际上是一样的效果。另外注意,聚合查询同样可以使用WHERE条件,因此我们可以方便地统计出有多少男生、多少女生、多少80分以上的学生等:

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

除了COUNT()函数外,SQL还提供了如下聚合函数:
SUM 计算某一列的合计值,该列必须为数值类型
AVG 计算某一列的平均值,该列必须为数值类型
MAX 计算某一列的最大值
MIN 计算某一列的最小值
MAX()和MIN()函数并不限于数值类型。如果是字符类型,MAX()MIN()会返回排序最后和排序最前的字符。要统计男生的平均成绩,我们用下面的聚合查询:

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

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

SELECT AVG(score) average FROM students WHERE gender = 'X';

在这里插入图片描述
对于聚合查询,SQL还提供了“分组聚合”的功能。我们观察下面的聚合查询:

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

在这里插入图片描述

执行这个查询,COUNT()的结果不再是一个,而是3个,这是因为,GROUP BY子句指定了按class_id分组,因此,执行该SELECT语句时,会把class_id相同的列先分组,再分别计算,因此,得到了3行结果。但是这3行结果分别是哪三个班级的,不好看出来,所以我们可以把class_id列也放入结果集中:

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

在这里插入图片描述
也可以使用多个列进行分组。例如,我们想统计各班的男生和女生人数:

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

在这里插入图片描述

多表查询

SELECT查询不但可以从一张表查询数据,还可以从多张表同时查询数据。查询多张表的语法是:SELECT * FROM <表1> <表2>。但多表查询的结果集可能非常巨大,要小心使用。例如,同时从students表和classes查询数据:

SELECT * FROM students, classes;

这种一次查询两个表的数据,查询的结果也是一个二维表,它是students表和classes表的“乘积”,即students表的每一行与classes表的每一行都两两拼在一起返回。结果集的列数是students表和classes表的列数之和,行数是students表和classes表的行数之积。我们发现合并后有两列id和name,这时我们可以用投影查询,给id和name起个别名;

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

多表查询也可添加where条件子句

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

连接查询

连接查询是另一种类型的多表查询,就是先确定一个主表作为结果集,然后,把其他表的行有选择性地“连接”在主表结果集上。JOIN查询需要先确定主表,然后把另一个表的数据“附加”到结果集上;INNER JOIN是最常用的一种JOIN查询,它的语法是SELECT ... FROM <表1> INNER JOIN <表2> ON <条件...>;例如我们希望结果集同时包含所在班级的名称,上面的结果集只有class_id列,缺少对应班级的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;

外连接(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;

注意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等子句。
注意:使用别名不是必须的,但可以更好地简化查询语句。

与外连接相比,我们可以发现内连接多了一行四班,但相关的列均为NULL。因为根据ON条件s.class_id = c.id,classes表的id=4的行正是“四班”,但是,students表中并不存在class_id=4的行。

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则返回左表都存在的行。
例如,如果我们给students表增加一行,并添加class_id=5,由于classes表并不存在id=5的行,所以,LEFT OUTER JOIN的结果会增加一行,对应的class_name是NULL:

INSERT INTO students (class_id, name, gender, score) values (5, '新生', 'M', 88);
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
LEFT OUTER JOIN classes c
ON s.class_id = c.id;

在这里插入图片描述
在这里插入图片描述

修改数据

INSERT(插入新纪录)

INSERT语句的基本语法是:

INSERT INTO <表名> (字段1, 字段2, ...) VALUES (1,2, ...)

例如,我们向students表插入一条新记录,先列举出需要插入的字段名称,然后在VALUES子句中依次写出对应字段的值:

INSERT INTO students (class_id, name, gender, score) VALUES (2, '大牛', 'M', 80);
SELECT * FROM students;

在这里插入图片描述
我们发现第一条语句中并未列出id字段,那是因为id字段是一个自增主键,它的值可以由数据库自己推算出来。此外,如果一个字段有默认值,那么在INSERT语句中也可以不出现。
注意:字段顺序不必和数据库表的字段顺序一致,但值的顺序必须和字段顺序一致。也就是说,可以写INSERT INTO students (score, gender, name, class_id) ...,但是对应的VALUES就得变成(80, 'M', '大牛', 2)

还可以一次性添加多条记录,只需要在VALUES子句中指定多个记录值,每个记录是由(…)包含的一组值:

INSERT INTO students (class_id, name, gender, score) VALUES
  (1, '大宝', 'M', 87),
  (2, '二宝', 'M', 81);

SELECT * FROM students;

UPDATE(更新已有记录)

UPDATE语句的基本语法是:

UPDATE <表名> SET 字段1=1, 字段2=2, ... WHERE ...;

例如,我们想更新students表id=1的记录的name和score这两个字段,先写出UPDATE students SET name='大牛', score=66,然后在WHERE子句中写出需要更新的行的筛选条件id=1

UPDATE students SET name='大牛', score=66 WHERE id=1;    
SELECT * FROM students WHERE id=1;

在这里插入图片描述
注意到UPDATE语句的WHERE条件和SELECT语句的WHERE条件其实是一样的,因此完全可以一次更新多条记录:

UPDATE students SET name='小牛', score=77 WHERE id>=5 AND id<=7;   
SELECT * FROM students;

在UPDATE语句中,更新字段时可以使用表达式。例如,把所有80分以下的同学的成绩加10分:

UPDATE students SET score=score+10 WHERE score<80;
SELECT * FROM students;

其中,SET score=score+10就是给当前行的score字段的值加上了10。

如果WHERE条件没有匹配到任何记录,UPDATE语句不会报错,也不会有任何记录被更新。例如:

UPDATE students SET score=100 WHERE id=999;
SELECT * FROM students;

UPDATE语句可以没有WHERE条件,例如:

UPDATE students SET score=60;

这时,整个表的所有记录都会被更新。所以,在执行UPDATE语句时要非常小心,最好先用SELECT语句来测试WHERE条件是否筛选出了期望的记录集,然后再用UPDATE更新。

在使用MySQL这类真正的关系数据库时,UPDATE语句会返回更新的行数以及WHERE条件匹配的行数。
例如,更新id=1的记录时:

mysql> UPDATE students SET name='大宝' WHERE id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

MySQL会返回1,可以从打印的结果Rows matched: 1 Changed: 1看到。
当更新id=999的记录时:

mysql> UPDATE students SET name='大宝' WHERE id=999;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

MySQL会返回0,可以从打印的结果Rows matched: 0 Changed: 0看到。

DELETE (删除已有记录)

DELETE语句的基本语法是:

DELETE FROM <表名> WHERE ...;

例如,我们想删除students表中id=1的记录:

DELETE FROM students WHERE id=1;
SELECT * FROM students;

注意到DELETE语句的WHERE条件也是用来筛选需要删除的行,因此和UPDATE类似,DELETE语句也可以一次删除多条记录:

DELETE FROM students WHERE id>=5 AND id<=7;
SELECT * FROM students;

如果WHERE条件没有匹配到任何记录,DELETE语句不会报错,也不会有任何记录被删除。例如:

DELETE FROM students WHERE id=999;
SELECT * FROM students;

最后,要特别小心的是,和UPDATE类似,不带WHERE条件的DELETE语句会删除整个表的数据:

DELETE FROM students;

这时,整个表的所有记录都会被删除。所以,在执行DELETE语句时也要非常小心,最好先用SELECT语句来测试WHERE条件是否筛选出了期望的记录集,然后再用DELETE删除。

在使用MySQL这类真正的关系数据库时,DELETE语句也会返回删除的行数以及WHERE条件匹配的行数。例如,分别执行删除id=1id=999的记录:

mysql> DELETE FROM students WHERE id=1;
Query OK, 1 row affected (0.01 sec)

mysql> DELETE FROM students WHERE id=999;
Query OK, 0 rows affected (0.01 sec)

MYSQL

管理MYSQL

数据库

在一个运行MySQL的服务器上,实际上可以创建多个数据库(Database)。要列出所有数据库,使用命令:

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| shici              |
| sys                |
| test               |
| school             |
+--------------------+

其中,information_schema、mysql、performance_schemasys是系统库,不要去改动它们。其他的是用户创建的数据库。
要创建一个新数据库,使用命令:

mysql> CREATE DATABASE test;
Query OK, 1 row affected (0.01 sec)

要删除一个数据库(即删除数据库中所有的表),使用命令:

mysql> DROP DATABASE test;
Query OK, 0 rows affected (0.01 sec)

对一个数据库进行操作时,要首先将其切换为当前数据库:

mysql> USE test;
Database changed

列出当前数据库的所有表,使用命令:

mysql> SHOW TABLES;
+---------------------+
| Tables_in_test      |
+---------------------+
| classes             |
| statistics          |
| students            |
| students_of_class1  |
+---------------------+

要查看一个表的结构,使用命令:

mysql> DESC students;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| class_id | bigint(20)   | NO   |     | NULL    |                |
| name     | varchar(100) | NO   |     | NULL    |                |
| gender   | varchar(1)   | NO   |     | NULL    |                |
| score    | int(11)      | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

还可以使用以下命令查看创建表的SQL语句:

mysql> SHOW CREATE TABLE students;
+----------+-------------------------------------------------------+
| students | CREATE TABLE `students` (                             |
|          |   `id` bigint(20) NOT NULL AUTO_INCREMENT,            |
|          |   `class_id` bigint(20) NOT NULL,                     |
|          |   `name` varchar(100) NOT NULL,                       |
|          |   `gender` varchar(1) NOT NULL,                       |
|          |   `score` int(11) NOT NULL,                           |
|          |   PRIMARY KEY (`id`)                                  |
|          | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 |
+----------+-------------------------------------------------------+
1 row in set (0.00 sec)

创建表使用CREATE TABLE语句,而删除表使用DROP TABLE语句:

mysql> DROP TABLE students;
Query OK, 0 rows affected (0.01 sec)

修改表就比较复杂。如果要给students表新增一列birth,使用:

ALTER TABLE students ADD COLUMN birth VARCHAR(10) NOT NULL;
rename table 旧表名 to 新表名;
alter table 旧表名 rename [as] 新表名

要修改birth列,例如把列名改为birthday,类型改为VARCHAR(20)

ALTER TABLE students CHANGE COLUMN birth birthday VARCHAR(20) NOT NULL;

要删除列,使用:

ALTER TABLE students DROP COLUMN birthday;
退出MYSQL

使用EXIT命令退出MySQL(EXIT仅仅断开了客户端和服务器的连接,MySQL服务器仍然继续运行):

mysql> EXIT
Bye

实用SQL语句

插入或替换

如果我们希望插入一条新记录(INSERT),但如果记录已经存在,就先删除原记录,再插入新记录。此时,可以使用REPLACE语句,这样就不必先查询,再决定是否先删除再插入:

REPLACE INTO students (id, class_id, name, gender, score) VALUES (1, 1, '小明', 'F', 99);

id=1的记录不存在,REPLACE语句将插入新记录,否则,当前id=1的记录将被删除,然后再插入新记录。

插入或更新

如果我们希望插入一条新记录(INSERT),但如果记录已经存在,就更新该记录,此时,可以使用INSERT INTO ... ON DUPLICATE KEY UPDATE ...语句:

INSERT INTO students (id, class_id, name, gender, score) VALUES (1, 1, '小明', 'F', 99) ON DUPLICATE KEY UPDATE name='小明', gender='F', score=99

id=1的记录不存在,INSERT语句将插入新记录,否则,当前id=1的记录将被更新,更新的字段由UPDATE指定。

插入或忽略

如果我们希望插入一条新记录(INSERT),但如果记录已经存在,就啥事也不干直接忽略,此时,可以使用INSERT IGNORE INTO ...语句:

INSERT IGNORE INTO students (id, class_id, name, gender, score) VALUES (1, 1, '小明', 'F', 99);

id=1的记录不存在,INSERT语句将插入新记录,否则,不执行任何操作。

快照

如果想要对一个表进行快照,即复制一份当前表的数据到一个新表,可以结合CREATE TABLE和SELECT

-- 对class_id=1的记录进行快照,并存储为新表students_of_class1:
CREATE TABLE students_of_class1 SELECT * FROM students WHERE class_id=1;

新创建的表结构和SELECT使用的表结构完全一致。

写入查询结果集

如果查询结果集需要写入到表中,可以结合INSERTSELECT,将SELECT语句的结果集直接插入到指定表中。
例如,创建一个统计成绩的表statistics,记录各班的平均成绩:

CREATE TABLE statistics (
    id BIGINT NOT NULL AUTO_INCREMENT,
    class_id BIGINT NOT NULL,
    average DOUBLE NOT NULL,
    PRIMARY KEY (id)
);

然后,我们就可以用一条语句写入各班的平均成绩:

INSERT INTO statistics (class_id, average) SELECT class_id, AVG(score) FROM students GROUP BY class_id;

确保INSERT语句的列和SELECT语句的列能一一对应,就可以在statistics表中直接保存查询的结果:

> SELECT * FROM statistics;
+----+----------+--------------+
| id | class_id | average      |
+----+----------+--------------+
|  1 |        1 |         86.5 |
|  2 |        2 | 73.666666666 |
|  3 |        3 | 88.333333333 |
+----+----------+--------------+
3 rows in set (0.00 sec)
强迫使用指定索引

在查询的时候,数据库系统会自动分析查询语句,并选择一个最合适的索引。但是很多时候,数据库系统的查询优化器并不一定总是能使用最优索引。如果我们知道如何选择索引,可以使用FORCE INDEX强制查询使用指定的索引。例如:

> SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC;

指定索引的前提是索引idx_class_id必须存在。

事务

Read Uncommited

Read Uncommitted是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。例如:
首先,我们准备好students表的数据,该表仅一行记录:

mysql> select * from students;
+----+-------+
| id | name  |
+----+-------+
|  1 | Alice |
+----+-------+
1 row in set (0.00 sec)

然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:
在这里插入图片描述
在这里插入图片描述

Read Committed

Read Committed隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
我们仍然先准备好students表的数据:

mysql> select * from students;
+----+-------+
| id | name  |
+----+-------+
|  1 | Alice |
+----+-------+

然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:
在这里插入图片描述
在这里插入图片描述

Repeatable Read

Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。
幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
我们仍然先准备好students表的数据:

mysql> select * from students;
+----+-------+
| id | name  |
+----+-------+
|  1 | Alice |
+----+-------+
1 row in set (0.00 sec)

然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:
在这里插入图片描述
在这里插入图片描述

Serializable

Serializable是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
虽然Serializable隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。
如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用InnoDB(支持事务安全的引擎,支持外键、行锁、事务是他的最大特点。),默认的隔离级别是Repeatable Read

  • 29
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

poggioxay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值