MySQL表的增删改查基本操作

x33g5p2x  于2022-02-16 转载在 Mysql  
字(9.7k)|赞(0)|评价(0)|浏览(401)

CRUD:
注释:在SQL中可以使用“–空格+描述”来表示注释说明
CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、删除(Delete)四个单词的首字母缩写

一、新增(create) 基础

语法:

INSERT [INTO] table_name
 [(column [, column] ...)] 
 VALUES (value_list) [, (value_list)] ...
 
value_list: value, [, value] ...

示例:

-- 创建一张学生表
CREATE TABLE student (
   id INT,
   sn INT comment '学号',
   name VARCHAR(20) comment '姓名',
   qq_mail VARCHAR(20) comment 'QQ邮箱'
);

1. 单行数据 + 全列插入

-- 插入两条数据,value_list 数量必须和定义表的数量及顺序一致
INSERT INTO student VALUES (100, 10000, '唐三藏', NULL);
INSERT INTO student VALUES (101, 10001, '孙悟空', '11111');

2.多行数据 + 指定列插入

-- 插入两条记录,value_list 数量必须和指定列数量及顺序一致
INSERT INTO student (id, sn, name) VALUES
 (102, 20001, '曹孟德'),
 (103, 20002, '孙仲谋');

当显示OK,后面的sec单位为秒,插入操作相对其它是比较慢的.

因此:关系型数据库由于对于数据要进行大量的校验,所以牺牲了性能,换来的是数据的完整性和可靠性.
正因为关系型数据库性能比较低,在一个高并发的系统中,数据库很容易就成为性能瓶颈,数据库也是在极端情况下最容易挂的服务.

二、查询(Retrieve)基础

注:所有的select操作都不会对原来的表造成任何改变,基于原来的表,生成结果表。大部分的select操作的查找结果是无法和原来的表记录一 一对应的。
语法:

SELECT
 [DISTINCT] {* | {column [, column] ...} 
 [FROM table_name]
 [WHERE ...]
 [ORDER BY column [ASC | DESC], ...]
 LIMIT ...

1.查找所有列

使用select * from [表名],"*"代表的是通配符,它的意思是查找所有列.
原来的test1数据:

指定列插入:

再次查询test1中的数据:

2.查找指定的列

使用select [列名] from 表名;
原来表中的数据:

只查找id和name的列:

3.查询字段为表达式,并进行计算

查询字段为表达式,针对查到的列进行一定的表达式计算.
语法:select [列名+列名] from test1;
a)例如:查找所有同学的名字和总成绩:
注意:MySQL中null加任何数字结果都为null

b)查找所有同学的语文成绩,并在其基础上加10分:

c) 查询字段使用别名:为查询结果中的列指定别名,表示返回的结果集中,以别名作为该列的名称,语法:SELECT column [AS] alias_name [...] FROM table_name;

-- 结果集中,表头的列名=别名
SELECT id, name, chinese + math + english 总分 FROM exam_result;

as 后面加的是别名的名字

4.去重

a)使用DISTINCT关键字对某列数据进行去重.

首先,孙悟空和白骨精的chinese是相同的,此时进行去重操作:

b)也可以指定多列去重,即如果两列互相之间有相同的,则能够去重。
示例:

注意:
使用distinct的时候,必须把对应的列都放到distinct之后。
去重查找得到的结果表的行数和原来的可能不一样

5.排序

语法:

-- ASC 为升序(从小到大)
-- DESC 为降序(从大到小)
-- 默认为 ASC
SELECT ... FROM table_name [WHERE ...] 
 ORDER BY column [ASC|DESC], [...];

a) 对某一列进行升序排序
示例如下:

b)对某一列进行降序排序

c)对某几列进行计算的基础上进行排序

d)对某列进行计算后排序并使用别名
赋予别名后可以直接order by 别名 来排序。

d)按照多个列进行排序。
先把所有同学的信息按照语文降序排序,再按照数学降序排序,再按照英语降序排序。例如当前两个同学的语文、数学成绩相同,最后排序的位置依赖于英语成绩的排序高低决定。

列越靠前,优先级越高。

注意:Null是所有值中最小的,如果按升序排序则它在最上方,如果按降序排序则它在最下方。

6.条件查询:where

1.条件查询使用到的运算符

比较运算符:

运算符说明
>, >=, <, <=大于,大于等于,小于,小于等于
=等于,NULL 不安全,例如 NULL = NULL 的结果是 NULL
<=>等于,NULL 安全,例如 NULL <=> NULL 的结果是 TRUE(1)
!=,<>不等于
BETWEEN a0 ANDa1范围匹配,[a0, a1],如果 a0 <= value <= a1,返回 TRUE(1)
IN(option,…)如果是option 中的任意一个,返回 TRUE(1)
IS NULL是 NULL
IS NOT NULL不是 NULL
LIKE模糊匹配。% 表示任意多个(包括 0 个)任意字符;_ 表示任意一个字符

逻辑运算符:

运算符说明
AND多个条件必须都为 TRUE(1),结果才是 TRUE(1)
OR任意一个条件为 TRUE(1), 结果为 TRUE(1)
NOT条件为 TRUE(1),结果为 FALSE(0)

自我整理:

2.条件查询示例

a) 查找数据中chinese为null的记录
原先的表:

要用<=>来识别NULL。

另一种方式:

b)查找英语成绩不及格的同学的信息(<60)

c)查找语文成绩比英语成绩好的同学信息

d)查找总分在200分以下的同学
注:在where后不能使用别名来操作。

e)查找语文成绩和英语成绩都小于80的同学

注:如果用到两个或两个以上的逻辑符号,可以加上括号来表明优先级,否则若and和or同时出现则and的优先级更高。

f)查询语文成绩在70到90之间的同学信息

between and 两边的数字一定是前面的比后面的数字大,并且两边都是闭区间。

或者:

g)找出数学成绩是78.5或87.5的同学的信息

当然也可以借助逻辑符号or来完成。

h) 模糊匹配
%是用来匹配任意个的任意字符
_ 是用来匹配一个任意字符

示例:
查找所有姓孙的同学的成绩:

注意:当使用下划线时,要使用两个下划线

模糊查找也可以用于数字。
如:
查找所有同学中语文成绩以8开头的信息

注意:模糊匹配的查询效率比较低,一般不太建议使用like。

i) 进行复杂条件查询的时候,存在一个“最左原则”。

查找所有同学中 姓孙 并且 语文成绩>60 的同学。

此处建议第二种写法,因为第二种写法在原表过滤掉不姓孙的同学时只剩下两个同学,此时再去过滤语文小于60的同学只是在剩余两个同学当中找。
而第一种写法在原表过滤掉语文成绩小于60的同学则还剩下4个同学(如下图),此时再去过滤掉不姓孙的同学要在这4个同学当中去找。

两种写法的第一次遍历都要遍历全表,因此第二种的写法效率更高。

总结:多个条件在一起联合生效时,一般要求哪个条件能过滤掉的数据多(剩下的数据少)就该把该条件放在最左侧。

3.分页查找

上面的select操作,除了条件查找外,其余的都不应该出现在生产服务器上直接执行。最保险的就是分页查找,相当于把查找结果只选取一小部分来作为结果。关键字limit。

示例:
a)查找所有同学中总分成绩最高的前三名。

b) 查找同学信息中总分最高的4到6名
关键字:limit和offset

offset后面的数字相当于下标,从该下标往后数limit后面的数字个(包括该下标本身算一个)。需要特别注意。

c)如果limit后面的数字过大,超过的记录的数目,返回结果不会有任何错误。
如:
原表中的数据:

limit后的数字过大对结果没影响:

d) offset后面的数字过大,得到的结果可能是一个空的结果

三、更新(update) 基础

语法:

UPDATE table_name SET column = expr [, column = expr ...]
 [WHERE ...] [ORDER BY ...] [LIMIT ...]

具体:

update [表名] set [列名]=[修改的值],[列名]=[修改的值] where 子句;

a) 把孙悟空的数学成绩改为90分
原来的表:

改完的表:

注意:如果不加where,则一整列都会被修改,一般搭配where使用。

update每次修改几行记录是不确定的,具体取决于where中的条件怎么写,得看where条件过滤后还剩下多少条记录。

b) 把所有同学的语文成绩都-10分。
原来的表:

进行修改:

改后的表:

d) 将总成绩倒数3名的同学的数学成绩加10分。
原来的表:

修改:

注:Rows matched后面的数字指的是where子句匹配后还剩多少行,Changed后面的数字指的是实际修改的行数。一般来说两个值会相等,但是如果对应内容为null可能会出现差异。

就好比上面的来说,有一个同学的成绩为null,每一行相加但null是不会操作的。
修改后:

注:修改数据时,数据不应该超过指定列的数据类型的范围。

四、删除 基础

语法:

DELETE FROM  table_name [WHERE ...] [ORDER BY ...] [LIMIT ..

具体:

delete from [表名] where [筛选条件]

示例:原来的表:
此时要删除同学中姓孙的全部信息:

删除后:

注:删除操作是很危险的,一旦数据被删了,通过常规手段是无法恢复的。

五、约束

1.约束类型

  • NOT NULL - 指示某列不能存储 NULL 值。
  • UNIQUE - 保证某列的每行必须有唯一的值。
  • DEFAULT - 规定没有给列赋值时的默认值。
  • PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
  • FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性。
  • CHECK - 保证列中的值符合指定的条件。对于MySQL数据库,对CHECK子句进行分析,但是忽略CHECK子句

1.1 null约束

目的是为了防止表中的数据最终不为null。
先创建一个表,我们此处设置id不能为空。

再去查看表的属性,可以看到id的Null列是NO。

当插入的数据为空时,有:

1.2 UNIQUE:唯一约束

先创建一个表,指定id列中的所有行不能重复

再去看表的属性,可以发现id中的Key为UNI,是unique的缩写。

若插入的id有重复的,则有:

如果要一个列要同时满足不为空并且该列的所有行不重复,则可以这样创建表:

观察表的属性:

这样创建表的效果下面介绍的主键的效果。

1.3 PRIMARY KEY:主键约束

创建一个表

插入一个id为空的值报的错误信息:

插入id相同的值报的错误信息:

1.4 default 给列执行默认值

创建一个表,让名字都给到默认值unknown。

查看表的属性:
id 的默认值为null,name的默认值为unknown。

此时插入一个元素,有:

1.5 auto_increment 保证主键不重复

auto_increment能够实现自增。

创建一个表:
在id设置主键约束后加上auto_increment,此时查看表的属性:

此时插入id为3的孙权和id为null的刘备,但是打印表后刘备的id紧加着孙权的id之后:

自增的特点:
1、如果表中没有任何记录,自增从1开始。
2、如果表中已经有记录了,自增从上一条记录往下自增。

但是有个特殊例外:如果把中间的某个数据删了之后,再插入的时候会根据前面删掉的自增主键的值不会重复被利用。
先将id为4的刘备删掉:

再插入id为null的刘备:此时刘备的id变为5.

但有同学会问:主键约束不是不能插入null吗,为什么insert的时候可以为null?
实际上在主键的auto_increment中插入null不是最终结果,而是让数据库自动生成新的自增值。

1.6 FOREIGN KEY 外键约束

外键的使用场景:

创建了一个class表:

创建了一个student表:

class表中插入的班级数据:

student表中插入学生的数据:

此时,student表中的classId字段的值应该与class表中的id值匹配才是逻辑正确的,虽然以上方法的插入也对,但是不科学。为了让此处的数据校验更严格,可以使用外键。

重新创建class表(与上面的创建方式不变):

重新创建student表:
外键的设置习惯放在最后。

在设置外键时需要指定三方面信息:
1.指定当前表中的哪一列关联。
2.指定和哪张表关联。
3.指定和目标表中的哪一列关联。

因此后续往student表中插入数据的时候,MySQL就会自动检查当前的classId字段的值是否在class表的id列中出现过。如果没有,则插入失败。

例子:
先向class中插入3个元素如下:

此时向student表中插入:

如果插入的学生的classId超过了class中的id,则会报错:

MUL则代表的是外键的意思:

注:
1.使用外键,会对插入操作的效率产生一定的影响。
2.外键约束会影响表的删除

如:class表被其它表关联着,此时是无法直接删除class表的。

如果真的把class删了,此时再对student的classId列进行任何操作都是无意义的。因为student表的插入依赖于class表。

1.7 CHECK约束(了解)

MySQL使用时不报错,但忽略该约束:

drop table if exists test_user;
create table test_user (
   id int,
   name varchar(20),
   sex varchar(1),
   check (sex ='男' or sex='女')
);

六、表的设计

1. 一对一

比如说一个人对应一个身份证号,就是一个一对一的关系。

2.一对多

组织形式是:
class表(id,name)
students表(id,name,classId)

MySQL中:student表中可能会出现很多记录,但这很多条记录中,classId可能是相同的,这些classId相同的记录就表示是存在于同一个班级。

但是用代码的组织是:class表(id,name,student),student列当成是一个数组,数组中每个元素表示一个学生的信息。但是MySQL中没有数组类型,这是平时写代码中的常见的表示方式。

3.多对多

例如:
当前有很多学生:甲乙丙丁。当前又有很多门课程:语文,数学,英语,物理,化学。任意一个学生,都可能会选择多门课程。任意一门课程,都可能会被多个学生所选择。

图示:

因此多对多的关系,可以引入中间表来解决问题。例如:描述每个同学的每个科目的信息。

首先创建一个student表和course表。

创建一个成绩表,里面包含学生的id,课程的id和成绩。意思是丁在语文这个科目上考了90分。当然成绩表也可以将学生id和课程id设置为外键更保守与严谨。

由于是多对多的关系,会看到courseId存在很多重复的(很多同学都可能修了这门课程),很多studentId也存在重复(一个同学可能修了多门课程)
因此可以这样创建:

七、新增(在原有表中获取数据导入另一个表)进阶

语法:

INSERT INTO table_name [(column [, column ...])] SELECT ...

示例:
先创建两个表,一个表先填入数据,另一个表为空:

之后将user中的name和decription属性用select关键字导入user2表中:

此时user2表中的name和decription和user中的一模一样:

注:select后面的子句为子查询,子查询得到列的数目、顺序、类型都得和被插入的表的列的数目、顺序、类型一致。列的名字一致不一致都无所谓。

如:
a) 将user的全部属性导入user2中是不可行的:
因为user中有三列,user2中只有两列。

b) 将user中的某两个属性不按照顺序来导入user2中,虽然导入成功,但是毫无意义。

八、查询 进阶

1.聚合查询

聚合查询:把查询结果中的若干数据合并在一起。

1.1 聚合函数

常见的统计总数、计算平局值等操作,可以使用聚合函数来实现,常见的聚合函数有:

函数说明
COUNT([DISTINCT] expr)返回查询到的数据的 数量
SUM([DISTINCT] expr)返回查询到的数据的 总和,不是数字没有意义
AVG([DISTINCT] expr)返回查询到的数据的 平均值,不是数字没有意义
MAX([DISTINCT] expr)返回查询到的数据的 最大值,不是数字没有意义
MIN([DISTINCT] expr)返回查询到的数据的 最小值,不是数字没有意义

示例:
下面这张图记录的是select * from user的行数。这样查询的开销更大。

下面这张图记录的是 select * from id 的行数。
两个只是碰巧相等。

注:
1、 count是一个函数,在敲代码的时候count后面要紧挨着括号,如果与括号中间多了空格,那否则就是一个列。
2、count本来是一个函数,如果count与括号之间多了空格,那么count会被当成一个列名。

当要求所有同学中分数低于90的平均分:

1.2 group by子句

SELECT 中使用 GROUP BY 子句可以对指定列进行分组查询。需要满足:使用 GROUP BY 进行分组查询时,SELECT 指定的字段必须是“分组依据字段”,其他字段若想出现在SELECT 中则必须包含在聚合函数中。

示例:
下面已经创建了一个emp表:

若要求出不同role的平均salary,则:角色相同的会被分到同一个组

有了group by后,就把role相同的记录放到同一组中,avg就是针对每个组分别来求平均值。

也可以多个函数一起使用:

group by中也可以结合一些条件对数据进行进一步的筛选。不是使用where,而是having。

1.3 having子句

GROUP BY 子句进行分组以后,需要对分组结果再进行条件过滤时,不能使用 WHERE 语句,而需要用HAVING

例如:显示出所有平均工资低于400的岗位和平均薪资

where和having的意义是不一样的:
where是针对原始表中的每条记录都进行筛选。
having是针对group by之后的结果进行筛选。

2. 联合查询

2.1 笛卡尔积

例如此时有两个表:

笛卡尔积:无非就是把不同表中的每列中的每个元素结合起来。下面这行命令的最后可以加上where子句,如果没有where则得到的结果就是两个表的笛卡尔积。

笛卡尔积处理的结果:

第一次的id由1到8是拿表2中的第一条记录和表1中的每一条记录进行匹配。第二次的id由1到8是拿表2中的第二条记录和表1中的每一条记录进行匹配。

此时我们发现有一些数据是没有意义的,如studentId对应的是学生的id,如果不对应则数据是没有意义的。比如第一行是有意义的:是黑旋风李逵的哪个成绩是70.5分。

因此,为了处理两个表中笛卡尔积结果后有些没有意义的数据,在原来的基础上添加where子句过滤掉没有意义的数据是很有必要的。

处理后的结果:

2.2 内连接

当进行笛卡尔积之后再按照id筛选,这样的筛选结果一定是同时在两张表中都出现过的记录。这样的操作称为内连接。
语法:

select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 and 其他条件;
select 字段 from 表1 别名1,表2 别名2 where 连接条件 and 其他条件;

a) 查找名为许仙的所有成绩
如果再按照处理过的笛卡尔积的结果查找同学名字为许仙的成绩,则可以:
这种方法是比较推荐的,很好理解。

还有例外一种方法:

因此面对表的联合查询的设计思路:
1.先把两张表联合在一起,得到笛卡尔积。
2.按照student id 对笛卡尔积的记录进行筛选,保留有意义的数据。
3.再针对名字进行筛选。

b) 查找所有同学的总成绩,以及该同学的基本信息
先直接进行联合查找,此时没有任何条件,得到的就是笛卡尔积。

再根据student中的id与score中的id对应,去除无意义的数据:

运行sum函数求出每个学生对应的总成绩,要用group by子句。

如果按照学生的名字来分组,id可能会不是按照顺序来排,因为group by 的过程类似于hash的过程。如果没有指定order by语句,得到的结果的顺序都是不确定的。

c) 查找所有同学的每一科成绩,和同学的相关信息。最终的效果需要显示:同学姓名,科目名称,对应的成绩

第一步:先针对三表进行联合,得到一个非常大的笛卡尔积,这个笛卡尔积中大部分数据都是无意义的。

第二步:按照student.id和score.course_id 针对笛卡尔积的数据进行筛选。

效果:

但是我们发现,course.id与score.course_id对不上,因此有部分数据还是没有意义的。因此要添加course.id=score.course_id 的条件。

第三步:此时已经是最简的笛卡尔积结果,按照要求要保留 同学姓名,科目名称,对应的成绩 ,因此其它不变,只需要改显示的列。

仔细观察发现:student表中本来有8个同学,最终查询的所有同学的成绩的时候发现,只有7个同学有成绩,“老外学中文”没有成绩。我们发现“老外学中文的学号是8”,而score表中,并没有学号为8的同学的成绩。

2.3 外连接

外连接分为左外连接和右外连接。如果联合查询,左侧的表完全显示我们就说是左外连接;右侧的表完全显示我们就说是右外连接。

了解内连接、外连接等更多连接的博客

语法:

-- 左外连接,表1完全显示
select 字段名  from 表名1 left join 表名2 on 连接条件;
-- 右外连接,表2完全显示
select 字段 from 表名1 right join 表名2 on 连接条件;

2.4 自连接

自连接是指在同一张表连接自身进行查询。

案例:
显示所有“计算机原理”成绩比“Java”成绩高的成绩信息。

已有的表:

步骤1:先找到java和计算机原理课程 id

步骤二:按照课程id在分数表中筛选数据
a) 针对score表自身进行笛卡尔积
有很多个数据并且是不符合我们题目要求的。

b) 加上学生id的限制。像第二行的数据就是符合要求的。

c) 加上课程id的限制

d) 按照分数大小进行比较。

2.5 子连接

2.5.1 单行子查询

a) 查询和“不想毕业”同班的同学有哪些
原表:

找到与“不想毕业”的同学名字:

2.5.2 多行子查询

查询“语文”或者“英文”课程对应的成绩:
原有的表:

a) 借助 in 的方式来进行子查询:先执行子查询(后面的括号中的查询称为子查询),把子查询的结果保存到内存当中,再进行主查询,再结合刚才子查询的结果来筛选最终结果。并且子查询只执行一次

b) 借助exists来完成子查询:先执行主查询,再触发子查询。子查询针对主表中的查询记录,都会再一次触发一次子查询,会执行很多次。相当于用时间换空间。

结论:
如果子表查询的结果集合比较小,就推荐使用in。
如果子表查询的结果集合比较大,而主表的集合小,就使用exists。

2.6 合并查询

在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。使用UNION和UNION ALL时,前后查询的结果集中,字段需要一致。

该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。

案例:查询id小于3,或者名字为“英文”的课程:

  • union
select * from course where id<3 
union 
select * from course where name='Java';
-- 或者使用or来实现
select * from course where id<3 or name='Java';

  • union all
select * from course where id<3 
union all
select * from course where name='Java';

相关文章