CRUD : Create(创建), Retrieve(读取),Update(更新),Delete(删除)
语法:
insert into tablename [(colname1,colname2…)] values(num1,num2,…);
eg:
先创建一个表,然后再向表里面插入数据:
然后向里面插入数据(正常插入:)
上面的插入方式,是按照指定列的方式来进行插入的,我们在插入的时候,可以不用指定要插入那些列的名字,而直接插入值,这时候这种插入方式叫做 ‘全列插入’:
当然我们也可以指定某些列的插入顺序,比如我就是要先插入年龄,在插入姓名、学号、性别,可以吗?当然可以:
对于可以缺省的,我们在插入的时候也可以不显示指定进行插入,比如本例中id是可以缺省的、性别、年龄也是可以缺省的,那么我们在插入的时候就可以不显示指定性别、id、年龄进行插入:
如果我们想一次性插入多行数据可以吗?
当然可以:
多行插入:
如果还想要插入更多行数据,直接添加多行数据即可:
一般情况下,我们插入主键或唯一键重复的数据时,数据会告诉我们键值冲突,为此我们会插入失败:
但是我们就是要插入values(‘司马光’,9,‘男’,90)这行数据怎么办?
我们可以利用语法:
insert ..... on duplicate key update colname1=xxx,colname2=yyy.....
on duplicate key:如果发生键值冲突
这句话的意思就是:如果当前插入的数据发生键值冲突,那么就将发生键值冲突行的数据更新为后面的数据:
比如上例中我们就是要插入:values(‘司马光’,9,‘男’,90)这行数据1,MySQL语句就可以这样写:
insert into students values('司马光',9,'男',90) on duplicate key update name='司马光',age=90,sex='男';
效果如图:
替换:
如果发生键值冲突,那么就将冲突行数据替换成后续的数据;
如果没有发生键值冲突,那么就不发生替换,直接进行插入;
语法:
replace into tablename (colname1,colname2,...) values(x1,x2,...)
eg:
语法:
SELECT [DISTINCT] {* | {column [, column] ...} [FROM table_name] [WHERE ...] [ORDER BY column [ASC | DESC], ...] LIMIT ...
eg:
创建表结构:
然后向里面插入一些数据:
如果我们要查询所有列的属性的话,我们可以按照以下方式:
select colnam1,colname2,..... from tablename;
对应本例中就是:
select id,name,chinese,math,english from exam_result;
但是这样查询所有列的话比较麻烦,要写出所有列的名称,为了简单,我们可以利用 ‘*’ 这个通配符来表示所有列,为此我们查询所有列的语句就可以变为:
select * from tablename;
eg:
但是在实际使用使用中,我们不建议直接使用全列查询,因为实际的数据库可能很大,我们使用全列查询的话,很可能导致我们的电脑刷屏,观察数据也不太清楚,其次,如果我们使用全列查询的话,就意味着我们需要使用更多的网络资源来进行传输数据;
指定列查询,顾名思义就是查询指定列的数据,没有显示指定查询的就不会显示:
eg:
指定列的数据不一定按照定义表的顺序来;
语法:
select colname1 [as] newcolname1,colname2 [as] newcolname2... from tablename
eg:
在MySQL中where就相当于C/C++语言中的if语句;
比较运算符:
运算符 | 说明 |
---|---|
>,>=,<,<= | 大于,大于等于,小于,小于等于 |
= | 等于(注意不是赋值),null不安全,也就是说=不能参与null的比较,比如:null=null的结果是null,1=null的结果也是null,而不是布尔值 |
<=> | 等于,null是安全的也就是说<=>可以参与null的比较,比如:null<=>null,结果是1;1<=>null,结果是0;也就是说如果参与等于运算的数据中可能含有null,我们建议用<=>来比较; |
!=,<> | 这两个不等于符号对于null来说都是不安全的,!=,<>都不能参与null的比较,比如:null!=null or null<>null 结果都是null;1!=null or 1<> null 结果都是null; |
between a0 and a1 | 范围匹配,[a0,a1],如果a0<=value<=a1,则返回true(1) |
in(option…) | value是option中的任意一个返回true(1) |
is null | value 是null?是,返回true(1) |
is not null | value 不是是null吗?不是,返回true(1) |
like | 模糊匹配.%表示任意多个字符(包括0个),_表示任意一个字符 |
逻辑运算符:
运算符 | 说明 |
---|---|
and | 逻辑与 |
or | 逻辑或 |
not | 逻辑取反 |
分析:
需要读取的列:
姓名、英语成绩;
帅选条件:英语成绩<60;
SQL语句:
select name,english from exam_result where english < 60;
分析:
需要读取的列:
姓名、语文成绩;
筛选条件: chinese大于等于80并且小于等于90;
SQL语句:
select name ,chinese from exam_result where chinese>=80 and chinese<= 90;
SQL语句还可以这样写:
select name,chinese from exam_result where chinese between 80 and 90;
分析:
需要读取的列:
姓名、数学成绩;
筛选条件:数学成绩是 58 或者 59 或者 98 或者 99 分;
SQL语句:
select name,math from exam_result where math=58 or math=59 or math=98 or math=99;
SQL语句还可以这样写:
select name,math from exam_result where math in(58,59,98,99);
分析:
需要读取的列:
姓名
筛选条件:姓孙的同学;
SQL语句:
select name from exam_result where name like '孙%';
分析:
需要读取的列:
姓名
筛选条件:孙某
SQL语句:
select name from exam_result where name like '孙_';
分析:
需要读取的列:
姓名、语文成绩、数学成绩;
筛选条件:语文成绩大于英语成绩
SQL语句:
select name,chinese,english from exam_result where chinese>english;
分析:
需要读取的列:
姓名、总分
筛选条件:总分<200
SQL语句:
select name,english+chinese+math from exam_result where english+chinese+math<200;
注意:
这里不能向以下这样写:
select name,english+chinese+math as 总分 from exam_result where 总分<200;
这样写是会报错的,报错原因是因为数据库不认识 ‘总分’ 这个字符,为什么呢?我们不是已经在前面已经将english+chinese+math重命名为 ‘总分’ 了嘛?按理来说 ‘总分’ 这个标识就等价于 english+chinese+math 啊,为什么还会报错?这就与MySQL执行的顺序先后相关了,在MySQL中是先执行读取操作操作在执行筛选操作?还是先执行筛选操作再执行读取操作?
很明显是先执行的筛选操作,MySQL在第一次遍历的时候就带着筛选条件来进行遍历的,这一次遍历就能拿到最后的结果!而如果我们是先遍历,再在遍历结果过中进行筛选的话,那么就会进行两次遍历,很明显存在浪费!因此MySQL选择那种方式就显而易见了,既然是先使用的筛选条件,可是筛选条件为 ‘总分’ < 200 这个’总分’并不存在于数据库列中啊,自然就报未知列名错误!我们这是属于先使用后重命名,数据库不认识是很正常的!
分析:
需要读取的列:
姓名、语文成绩
筛选条件:语文成绩大于80并且不姓孙;
SQL语句:
select name,chinese from exam_result where chinese > 80 and (not (name like '孙%'));
分析:
需要读取的列:
姓名、语文成绩、数学成绩、英语成绩、总成绩;
筛选条件: 孙某同学 或者 总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80
SQL语句:
select name,chinese,math,english ,math+chinese+english from exam_result where name like '孙_' or (math+chinese+english >200 and chinese
语法:
ASC:升序
DESC:降序
默认升序
select ... from tablename [where] order by colname [ASC|DESC];
注意:没有order by的语句返回的结果是未定义的,永远不要依赖,尽管它看起来有序!
测试表结构:
测试表中数据:
分析:
需要读取列:
姓名、数学成绩
筛选条件:无
排序:以数学成绩排升序
SQL语句:
select name,math from exam_from order by math ASC;
分析:
需要读取的列:
姓名、qq;
筛选条件:无;
排序:以qq号为升序或者降序;
SQL升序语句:
select name,qq from exam_result order by qq ASC;
我们可以发现null值比任何值都小,因此被排在最上面!
SQL降序语句:
select name,qq from exam_result order by qq DESC;
我们可以发现,null比任何值都要小,因此被排在最小面;
分析:
需读取的列:
姓名、数学成绩、英语成绩、语文成绩
筛选条件:无
排序:依次按 数学降序,英语升序,语文升序
SQL语句:
select name,math,english,chinese from exam_result order by math DESC ,english ASC,chinese ASC;
这个的应用场景就是:
首先按照数学成绩排序数学成绩高的排前面,数学成绩相等,则看英语成绩,英语成绩小的排前面,如果英语成绩还想等,则看语文成绩,语文成绩小的排前面,如果语文成绩还想等,看程序员自己的控制!
分析:
需读取的列:
姓名、总分
筛选条件:无
排序:按总分降序
SQL语句:
select name,english+math+chinese as 总分 from exam_result order by 总分 DESC;
注意:这里的话是先执行的读取,然后在进行的排序,也就是说重命名工作先进行,再进行的排序,因此在进行排序工作的时候数据库认识 ‘总分’ 就是 english+math+chinese 因此才没有报出未知列的错误!
分析:
需读取的列:
姓名、数学成绩
筛选条件:姓孙或者姓曹
排序:按数学成绩降序排序
SQL语句:
select name,math from exam_result where name like '孙%' or name like '曹%' order by math DESC;
语法:
起始下标为0;
select ...from tablename [where...] [order by...] limit n;
//从0开始读取n条记录;
select ...from tablename [where...] [order by...] limit s,n;
//从s开始读取n条记录;
select ...from tablename [where...] [order by...] limit n offset s;
//从s开始读取n条记录;
建议:对未知表进行查询时,最好加一条 LIMIT 1,避免因为表中数据过大,查询全表数据导致数据库卡死;
分析:
需要读取的列:
全列;
筛选条件:无
排序:id降序;
每页显示:3条
SQL语句:
第一页:
select * from exam_result order by id DESC limit 0,3;
第二页:
select * from exam_result order by id DESC limit 3,3
;
第三页:
select * from exam_result order by id DESC limit 6,3;
语法:
UPDATE table_name SET column = expr [, column = expr ...] [WHERE ...] [ORDER BY ...] [LIMIT ...]
使用案例:
分析:
更新列: 数学成绩;
更新条件: 孙悟空
SQL语句:
update exam_result set math=80 where name='孙悟空';
如果想要完成以上的效果的话,使用以下SQL也是可以的:
insert into exam_result values(2,'孙悟空',87,80,77,null) on duplicate key update math=80;
分析:
更新列: 数学、语文;
更新条件: 曹孟德
SQL语句:
update exam_result set math=60,chinese=70 where name ='曹孟德';
分析:
更新列: 数学
更新条件: 总成绩倒数前3
SQL语句:
update exam_result set matn=math+30 order by english +math +chinese ASC limiit 0,3;
分析;
更新列: 语文条件
更新条件: 无
SQL语句:
update exam_result set chinese =chinese*2;
语法:
DELETE FROM table_name [WHERE ...] [ORDER BY ...] [LIMIT ...]
测试案例:
分析:
删除列: 所有列
删除条件: 孙悟空
SQL语句:
delete from exam_result where name='孙悟空';
删除列: 所有列
删除条件: 无
SQL语句:
delete from exam_result;
这个语句比较危险,为了后面能够更好的做案例,我们使用一些测试用例来尝试:
在使用delete删除表中所有数据过后,auto_increment的值不会重置:
语法:
TRUNCATE [TABLE] table_name
这个语句与delete from tablename;
效果类似都是删除表中的所有数据,但是这个删除与与delete删除语句有点不同:
语法:
INSERT INTO table_name [(column [, column ...])] SELECT ...;
聚合函数:就是MySQL专门针对一组数据进行统计的内置函数;
聚合函数 | 功能 |
---|---|
count([distinct]) expr | 返回某列的行数 |
sum([distinct] expr) | 返回某列的总和,遇到NULL会忽略,不是数字没有意义 |
avg([distinct] expr) | 返回某一列的平均值,不是数字没有意义 |
max([distinct] expr ) | 返回某一列的最大值,不是数字没有意义 |
min([distinct] expr) | 返回某一列的最小值,不是数字没有意义 |
SQL语句:
select count(*) from exam_result;
SQL语句:
select count(qq) from exam_result;
当count具体统计某一列数据时,如果这一列数据为null时,则不会被统计!
SQL语句:
select count(math) from exam_result;
SQL语句:
select count(distinct math) from exam_result;
SQL语句:
select sum(english+math+chinese ) /count(*) from exam_result;
或者也可以使用avg聚合函数:
SQL语句:
select avg(english+chinese+math) from exam_result;
SQL语句:
select max(english) from exam_result;
在select语句中使用group by 子句可以对指定列进行分组查询;
语法:
select .... from tablename [where] group by colname1,colname2...
准备工作,创建一个雇员信息表(来自oracle 9i的经典测试表)
emp员工表
dept部门表
salgrade工资等级表
DROP database IF EXISTS `scott`;
CREATE database IF NOT EXISTS `scott` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `scott`;
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`deptno` int(2) unsigned zerofill NOT NULL COMMENT '部门编号',
`dname` varchar(14) DEFAULT NULL COMMENT '部门名称',
`loc` varchar(13) DEFAULT NULL COMMENT '部门所在地点'
);
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
`empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',
`ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',
`job` varchar(9) DEFAULT NULL COMMENT '雇员职位',
`mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',
`hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',
`sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',
`comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',
`deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);
DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (
`grade` int(11) DEFAULT NULL COMMENT '等级',
`losal` int(11) DEFAULT NULL COMMENT '此等级最低工资',
`hisal` int(11) DEFAULT NULL COMMENT '此等级最高工资'
);
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);
emp员工表
分析:
select avg(sal),max(sal) from emp group by deptno;
如果我们想知道是哪个部分的平均工资和最高工资,我们在读取的时候可以读取工作名;
SQL语句:
select deptno ,avg(sal),max(sal) from emp group by deptno;
SQL语句:
select deptno,job ,avg(sal),min(sal) from emp group by deptno,job;
分析:
1、先读取每个部门的平均工资:
SQL语句:
select deptno ,avg(sal) from emp group by deptno;
2、然后从上表的结果中筛选出平均工资低于2000的数据;
SQL语句:
select deptno ,avg(sal) as 平均工资 from emp group by deptno having 平均工资<2000;
这里我们用到了having子句,having 子句与where子句一样都是起筛选作用的,但是二者却有所不同:
having子句与where子句的区别:
- where子句和having子句都是筛选子句具有筛选功能;
- where 子句筛选的基本单位是条,having子句的删选基本单位是组;
- where子句的执行顺序优先于having子句;
- where子句中不可以是用聚合函数,而having子句中可以使用聚合函数
- where子句中无法使用select 子句中的别名,而having子句可以;
这里我们举个例子:
我们可以来查询一下,将SCOTT与KING这两个人排除在外的 平均工资低于2000的部门和它的平均工资:
1、SCOTT与KING排除在外的每个部门的平均工资:
SQL语句:
select deptno,avg(sal) as 平均工资 from emp where ename!='SCOTT' and ename!='KING' group by deptno;
2、基于上面这个分组查询的结果,我们利用having筛选出符合条件的数据:
select deptno,avg(sal) as 平均工资 from emp where ename!='SCOTT' and ename!='KING' group by deptno having 平均工资<2000;
我们来解析以下上面的SQL语句的执行顺序:
面试题:SQL查询中各个关键字的执行先后顺序 from > on> join > where > group by > with > having > select> distinct > order by > limit
实际上MySQL最大的难点之一就是:select的编码顺序并不是SQL语句的执行顺序,为了编写SQL语句更加流畅,我们可以根据子句的优先级来编写SQL语句,这样有助于我们理解SQL语句的执行逻辑;
关于MySQL的执行顺序,可以参考一下博主的博客:
https://blog.csdn.net/dz77dz/article/details/115111559?spm=1001.2014.3001.5506