数据库(数据库管系统:简单的sql语句)
常见的数据库管理系统有哪些?
Oracle MySQL DB2 Sybase "MS SqlServer 支持标准sql的数据库管理系统"
如何登录MySQL?
在命令窗口输入 mysql -uroot -p密码
或者为了不让别人看见密码,输入 mysql -uroot -p 然后直接回车 再输入密码
mysql修改密码或者忘记密码该怎么做?
看文章“mysql修改密码或者忘记密码该怎么做”
DB:DataBase(数据库,数据库实际上在硬盘上以文件的形式存在)
DBMS:DataBase Management System(数据库管理系统,常见的有:MySQL Oracle DB2 Sybase SqlServer)
sql:结构化程序查询语言,是一门标准通用语言。标准的sql适用于所有的数据库产品。
sql属于高级语言。只要能看懂英语单词的,写出来的sql语句,可以读懂什么意思。
sql语句在执行的时候,实际上内部也会先进行编译,然后再执行sql。
sql语句的编译由DBMS完成。
DBMS负责执行sql语句,通过执行sql语句来操作DB中的数据。
表:table 是数据库的基本组成单元,所有的数据都以表格的形式组织,因为表的可读性强。
一个表包括行和列:
行:被称为数据/记录(data)
列:被称为字段(column)
如下表:
学号 姓名 年龄
------------------------------------
110 张三 20
120 李四 21
每一个字段应该包括哪些属性?
字段名、数据类型、相关的约束。
如第一列:
字段名:学号
数据类型:int
相关的约束:学号不能超过多少。。。
如第二列:
字段名:姓名
数据类型:varchar(variable character可变字符串)(即String)
相关的约束:姓名不能为空等
学习MySQL主要还是学习通用的SQL语句,SQL语句包括增删改查,SQL语句怎么分类呢?
DQL(数据查询语言): 查询语句,凡是select语句都是DQL。
DML(数据操作语言):对表当中的数据进行增删改,有insert delete update。
DDL(数据定义语言):对表结构的增删改,有create drop alter。
TCL(事务控制语言):commit提交事务,rollback回滚事务。
DCL(数据控制语言): grant授权、revoke撤销权限等。
DQL:Data Query Language 数据库查询语言。
关键字:SELECT ... FROM ... WHERE。
DML:Data Manipulation Language 数据操纵语言。
关键字:INSERT、UPDATE、DELETE。
DDL:Data Definition Language 数据库模式定义语言。
关键字:CREATE,DROP,ALTER。
TCL:Transaction Control Language 事务控制语言。
关键字:COMMIT、ROLLBACK、SAVEPOINT。
DCL:Data Control Language 数据控制语言。
关键字:GRANT、REVOKE。
DDL,DML,DCL,DQL,TCL共同组成数据库的完整语言。
(后期大家练习的时候使用这个演示的数据)
第一步:登录mysql数据库管理系统
dos命令窗口:mysql -uroot -p密码
第二步:查看有哪些数据库
show databases; (这个不是SQL语句,属于MySQL的命令。)
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
第三步:创建属于我们自己的数据库
create database bjpowernode; (这个不是SQL语句,属于MySQL的命令。)
第四步:使用bjpowernode数据(即进入bjpowernode数据库)
use bjpowernode; (这个不是SQL语句,属于MySQL的命令。)
第五步:查看当前使用的数据库中有哪些表?
show tables; (这个不是SQL语句,属于MySQL的命令。)
第六步:初始化数据(将事先准备好的txt文件中的数据放入表中,即source+文件路径或者把文件拖进命令窗口会自动生成文件路径)
mysql> source D:\course\05-MySQL\resources\bjpowernode.sql
注意:数据初始化完成之后,有三张表:
+-----------------------+
| Tables_in_bjpowernode |
+-----------------------+
| dept |
| emp |
| salgrade |
+-----------------------+
什么是sql脚本呢?
bjpowernode.sql,这个文件以sql结尾,这样的文件被称为“sql脚本”。
当一个文件的扩展名是.sql,并且该文件中编写了大量的sql语句,我们称这样的文件为sql脚本。
注意:当sql脚本文件太大时,用记事本是打不开的。
这时可以在DOS命令窗口中,直接使用source+sql脚本文件路径,再回车就直接把数据添加进了。
source命令
drop database bjpowernode;
数据库bjpowernode 里面有三张表
+-----------------------+
| Tables_in_bjpowernode |
+-----------------------+
| dept | (部门表)
| emp | (员工表)
| salgrade | (工资等级表)
+-----------------------+
查看每张表的结构命令:desc+表的名称
mysql> desc dept;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| DEPTNO | int(2) | NO | PRI | NULL | | 部门编号
| DNAME | varchar(14) | YES | | NULL | | 部门名称
| LOC | varchar(13) | YES | | NULL | | 部门位置
+--------+-------------+------+-----+---------+-------+
mysql> desc emp;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| EMPNO | int(4) | NO | PRI | NULL | | 员工编号
| ENAME | varchar(10) | YES | | NULL | | 员工姓名
| JOB | varchar(9) | YES | | NULL | | 工作岗位
| MGR | int(4) | YES | | NULL | | 上级领导编号
| HIREDATE | date | YES | | NULL | | 入职日期
| SAL | double(7,2) | YES | | NULL | | 月薪
| COMM | double(7,2) | YES | | NULL | | 补助/津贴
| DEPTNO | int(2) | YES | | NULL | | 部门编号
+----------+-------------+------+-----+---------+-------+
mysql> desc salgrade;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| GRADE | int(11) | YES | | NULL | | 等级
| LOSAL | int(11) | YES | | NULL | | 最低薪资
| HISAL | int(11) | YES | | NULL | | 最高薪资
+-------+---------+------+-----+---------+-------+
查看表中的数据命令:select * from 表名称;
mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
mysql> select * from dept;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
mysql> select * from salgrade;
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
以下不是sql语句:
创建数据库:create database 数据库名称;
查询当前使用的数据库:select database();
查询数据库版本可以使用 select version();
终止一条语句:\c
退出mysql:exit
查看现有哪些数据库:show databases;
使用某个数据库:use 数据库名称;
查看当前使用哪个数据库:select database();
查看当前数据库中有哪些表:show tables;
查看其他数据库中有哪些表:show tables from 数据库名称;
查看表的结构:desc 表名称;
查看一个表的创建语句:show create table 表名称;
语法格式:
select 字段名1,字段名2,字段名3,.... from 表名;
提示:
1、任何一条sql语句以“;”结尾。
2、sql语句不区分大小写。
查询员工的年薪?(字段可以参与数学运算。)
select ename,sal * 12 from emp;
+--------+----------+
| ename | sal * 12 |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
给查询结果的列sal*12重命名?
select ename,sal * 12 as yearsal from emp;
+--------+----------+
| ename | yearsal |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
别名中有中文?
select ename,sal * 12 as 年薪 from emp; // 错误
select ename,sal * 12 as '年薪' from emp;
+--------+----------+
| ename | 年薪 |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
注意:标准sql语句中要求字符串使用单引号括起来。虽然mysql支持双引号,尽量别用。
as关键字可以省略?
mysql> select empno,ename,sal * 12 yearsal from emp;
+-------+--------+----------+
| empno | ename | yearsal |
+-------+--------+----------+
| 7369 | SMITH | 9600.00 |
| 7499 | ALLEN | 19200.00 |
| 7521 | WARD | 15000.00 |
| 7566 | JONES | 35700.00 |
| 7654 | MARTIN | 15000.00 |
| 7698 | BLAKE | 34200.00 |
| 7782 | CLARK | 29400.00 |
| 7788 | SCOTT | 36000.00 |
| 7839 | KING | 60000.00 |
| 7844 | TURNER | 18000.00 |
| 7876 | ADAMS | 13200.00 |
| 7900 | JAMES | 11400.00 |
| 7902 | FORD | 36000.00 |
| 7934 | MILLER | 15600.00 |
+-------+--------+----------+
查询所有字段?
select * from emp; // 实际开发中不建议使用*,效率较低。
语法格式:
select
字段,字段...
from
表名
where
条件;
执行顺序:先from,然后where,最后select
查询工资等于5000的员工姓名?
select ename from emp where sal = 5000;
+-------+
| ename |
+-------+
| KING |
+-------+
查询SMITH的工资?
select sal from emp where ename = 'SMITH'; //字符串使用单引号括起来。
+--------+
| sal |
+--------+
| 800.00 |
+--------+
找出工资高于3000的员工?
select ename,sal from emp where sal > 3000;
select ename,sal from emp where sal >= 3000;
select ename,sal from emp where sal < 3000;
select ename,sal from emp where sal <= 3000;
找出工资不等于3000的?
方法一:
select ename,sal from emp where sal <> 3000; //不等于3000即大于小于3000
方法二:
select ename,sal from emp where sal != 3000;
找出工资在1100和3000之间的员工,包括1100和3000?
方法一:
select ename,sal from emp where sal >= 1100 and sal <= 3000;
方法二:
select ename,sal from emp where sal between 1100 and 3000;
注意:between...and...是闭区间 [1100 ~ 3000]
注意:between and在使用的时候必须左小右大。
select ename,sal from emp where sal between 3000 and 1100; //查询不到任何数据
注意:between and除了可以使用在数字方面之外,还可以使用在字符串方面。
但此时应用在字符串方面是:左闭右开。
select ename from emp where ename between 'A' and 'C';
+-------+
| ename |
+-------+
| ALLEN |
| BLAKE |
| ADAMS |
+-------+
找出哪些人津贴为NULL?
在数据库当中NULL不是一个值,代表什么也没有,为空。
空不是一个值,不能用等号衡量。
必须使用 is null或者is not null
select ename,sal,comm from emp where comm is null;
+--------+---------+------+
| ename | sal | comm |
+--------+---------+------+
| SMITH | 800.00 | NULL |
| JONES | 2975.00 | NULL |
| BLAKE | 2850.00 | NULL |
| CLARK | 2450.00 | NULL |
| SCOTT | 3000.00 | NULL |
| KING | 5000.00 | NULL |
| ADAMS | 1100.00 | NULL |
| JAMES | 950.00 | NULL |
| FORD | 3000.00 | NULL |
| MILLER | 1300.00 | NULL |
+--------+---------+------+
select ename,sal,comm from emp where comm = null; //错误,没有 =null
Empty set (0.00 sec)
找出哪些人津贴不为NULL?
select ename,sal,comm from emp where comm is not null;
+--------+---------+---------+
| ename | sal | comm |
+--------+---------+---------+
| ALLEN | 1600.00 | 300.00 |
| WARD | 1250.00 | 500.00 |
| MARTIN | 1250.00 | 1400.00 |
| TURNER | 1500.00 | 0.00 |
+--------+---------+---------+
找出哪些人没有津贴?
select ename,sal,comm from emp where comm is null or comm = 0;
+--------+---------+------+
| ename | sal | comm |
+--------+---------+------+
| SMITH | 800.00 | NULL |
| JONES | 2975.00 | NULL |
| BLAKE | 2850.00 | NULL |
| CLARK | 2450.00 | NULL |
| SCOTT | 3000.00 | NULL |
| KING | 5000.00 | NULL |
| TURNER | 1500.00 | 0.00 |
| ADAMS | 1100.00 | NULL |
| JAMES | 950.00 | NULL |
| FORD | 3000.00 | NULL |
| MILLER | 1300.00 | NULL |
+--------+---------+------+
找出工作岗位是MANAGER和SALESMAN的员工?
select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
+--------+----------+
| ename | job |
+--------+----------+
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| JONES | MANAGER |
| MARTIN | SALESMAN |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| TURNER | SALESMAN |
+--------+----------+
and和or联合起来用:找出薪资大于1000的并且部门编号是20或30部门的员工。
错误的:select ename,sal,deptno from emp where sal > 1000 and deptno = 20 or deptno = 30;
正确的:select ename,sal,deptno from emp where sal > 1000 and (deptno = 20 or deptno = 30);
注意:当运算符的优先级不确定的时候加小括号。
in等同于or,但语法不同:找出工作岗位是MANAGER和SALESMAN的员工?
select ename,job from emp where job = 'SALESMAN' or job = 'MANAGER';
select ename,job from emp where job in('SALESMAN', 'MANAGER');
注意:in后面的值不是区间,是具体的值:
select ename,job from emp where sal in(800, 5000); //工资=800的和=5000的
+-------+-----------+
| ename | job |
+-------+-----------+
| SMITH | CLERK |
| KING | PRESIDENT |
+-------+-----------+
not in: 不在这几个值当中。
select ename,job from emp where sal not in(800, 5000);
+--------+----------+
| ename | job |
+--------+----------+
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| JONES | MANAGER |
| MARTIN | SALESMAN |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| SCOTT | ANALYST |
| TURNER | SALESMAN |
| ADAMS | CLERK |
| JAMES | CLERK |
| FORD | ANALYST |
| MILLER | CLERK |
+--------+----------+
模糊查询like(像):
注意:在模糊查询当中,必须掌握两个特殊的符号,一个是%,一个是_
%代表任意多个字符,_代表任意1个字符。
找出名字当中含有O的?
select ename from emp where ename like '%O%';
+-------+
| ename |
+-------+
| JONES |
| SCOTT |
| FORD |
+-------+
找出名字中第二个字母是A的?
select ename from emp where ename like '_A%';
+--------+
| ename |
+--------+
| WARD |
| MARTIN |
| JAMES |
+--------+
找出名字中有下划线的?
首先创建一个新表t_user:
+------+----------+
| id | name |
+------+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | WANG_WU |
+------+----------+
然后查找:
select name from t_user where name like '%_%'; //错误,_代表一个字符
+----------+
| name |
+----------+
| zhangsan |
| lisi |
| WANG_WU |
+----------+
select name from t_user where name like '%\_%';//正确,加一个转义字符\
+---------+
| name |
+---------+
| WANG_WU |
+---------+
找出名字中最后一个字母是T的?
select ename from emp where ename like '%T';
+-------+
| ename |
+-------+
| SCOTT |
+-------+
按照工资升序,找出员工名和薪资?
语法:
select
ename,sal
from
emp
order by
sal;
+--------+---------+
| ename | sal |
+--------+---------+
| SMITH | 800.00 |
| JAMES | 950.00 |
| ADAMS | 1100.00 |
| WARD | 1250.00 |
| MARTIN | 1250.00 |
| MILLER | 1300.00 |
| TURNER | 1500.00 |
| ALLEN | 1600.00 |
| CLARK | 2450.00 |
| BLAKE | 2850.00 |
| JONES | 2975.00 |
| FORD | 3000.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
+--------+---------+
注意:默认是升序。怎么指定升序或者降序呢?asc表示升序,desc表示降序。
select ename,sal from emp order by sal; // 升序
select ename,sal from emp order by sal asc; // 升序
select ename,sal from emp order by sal desc; // 降序。
按照工资的降序排列:
select ename,sal from emp order by sal desc;
当工资相同的时候再按照名字的升序排列:
select ename,sal from emp order by sal desc,ename asc;
注意:越靠前的字段越能起到主导作用。只有当前面的字段无法完成排序的时候,才会启用后面的字段。
注意:select * from emp order by 6;
//根据表的第6列排序,但是如果原表的第6列内容变了,每次排序都不一样,所以最好不要这样。
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
找出工作岗位是SALESMAN的员工,并且要求按照薪资的降序排列。
select
ename,job,sal
from
emp
where
job = 'SALESMAN'
order by
sal desc;
+--------+----------+---------+
| ename | job | sal |
+--------+----------+---------+
| ALLEN | SALESMAN | 1600.00 |
| TURNER | SALESMAN | 1500.00 |
| WARD | SALESMAN | 1250.00 |
| MARTIN | SALESMAN | 1250.00 |
+--------+----------+---------+
select
字段 3
from
表名 1
where
条件 2
order by
.... 4
order by是最后执行的。
什么是单行处理函数?
输入一行,输出一行(对表中每一行的数据进行处理)
计算每个员工的年薪?
select ename,(sal+comm)*12 as yearsal from emp; //错误
重点:所有数据库都是这样规定的,只要有NULL参与的运算结果一定是NULL。
sal不是NULL,而comm是NULL,则结果为NULL,所以上述错误。
正确:使用ifnull函数:
select ename,(sal+ifnull(comm,0))*12 as yearsal from emp;
单行处理函数:ifnull(xxx,xxx) (空处理函数)对可能为NULL的数据进行预处理
ifnull(可能为NULL的字段名,若为空则被当做什么处理)
select ename,ifnull(comm,0) as comm from emp;
+--------+---------+
| ename | comm |
+--------+---------+
| SMITH | 0.00 |
| ALLEN | 300.00 |
| WARD | 500.00 |
| JONES | 0.00 |
| MARTIN | 1400.00 |
| BLAKE | 0.00 |
| CLARK | 0.00 |
| SCOTT | 0.00 |
| KING | 0.00 |
| TURNER | 0.00 |
| ADAMS | 0.00 |
| JAMES | 0.00 |
| FORD | 0.00 |
| MILLER | 0.00 |
+--------+---------+
count 计数
sum 求和
avg 平均值
max 最大值
min 最小值
记住:所有的分组函数都是对“某一组”数据进行操作的。
找出工资总和?
select sum(sal) from emp;
+----------+
| sum(sal) |
+----------+
| 29025.00 |
+----------+
找出最高工资?
select max(sal) from emp;
+----------+
| max(sal) |
+----------+
| 5000.00 |
+----------+
找出最低工资?
select min(sal) from emp;
+----------+
| min(sal) |
+----------+
| 800.00 |
+----------+
找出平均工资?
select avg(sal) from emp;
+-------------+
| avg(sal) |
+-------------+
| 2073.214286 |
+-------------+
找出总人数?
select count(*) from emp;
或者:select count(ename) from emp;
+--------------+
| count(ename) |
+--------------+
| 14 |
+--------------+
注意:分组函数一共只有5个。
分组函数还有另一个名字:多行处理函数。
多行处理函数的特点:输入多行,最终输出的结果是1行。(表中数据只有14行,最后输出1行)
注意:分组函数自动忽略NULL。
select count(comm) from emp;
+-------------+
| count(comm) |
+-------------+
| 4 |
+-------------+
select sum(comm) from emp;
+-----------+
| sum(comm) |
+-----------+
| 2200.00 |
+-----------+
因此,不需要额外添加这个过滤条件。sum函数自动忽略NULL:
select sum(comm) from emp where comm is not null;
count(*)和count(具体的某个字段),他们有什么区别?
count(*):不是统计某个字段中数据的个数,而是统计总记录条数。(和某个字段无关)
+----------+
| count(*) |
+----------+
| 14 |
+----------+
count(comm): 表示统计comm字段中不为NULL的数据总数量。
+-------------+
| count(comm) |
+-------------+
| 4 |
+-------------+
分组函数也能组合起来用:
select count(*),sum(sal),avg(sal),max(sal),min(sal) from emp;
+----------+----------+-------------+----------+----------+
| count(*) | sum(sal) | avg(sal) | max(sal) | min(sal) |
+----------+----------+-------------+----------+----------+
| 14 | 29025.00 | 2073.214286 | 5000.00 | 800.00 |
+----------+----------+-------------+----------+----------+
//看完分组查询再看下面这个问题。
找出工资高于平均工资的员工?
select avg(sal) from emp; // 平均工资
+-------------+
| avg(sal) |
+-------------+
| 2073.214286 |
+-------------+
select ename,sal from emp where sal > avg(sal); //ERROR 1111 (HY000): Invalid use of group function
思考以上的错误信息:无效的使用了分组函数?(分组=group)
原因:SQL语句当中有一个语法规则,分组函数不可直接使用在where子句当中。
怎么解释?
因为group by是在where执行之后才会执行的。
先分组group by,才能使用分组函数。先where后group by。
select 5
..
from 1
..
where 2
..
group by 3
..
having 4
..
order by 6
..
找出工资高于平均工资的员工?
第一步:找出平均工资
select avg(sal) from emp;
+-------------+
| avg(sal) |
+-------------+
| 2073.214286 |
+-------------+
第二步:找出高于平均工资的员工
select ename,sal from emp where sal > 2073.214286;
+-------+---------+
| ename | sal |
+-------+---------+
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| FORD | 3000.00 |
+-------+---------+
把上面两步并为一步:select ename,sal from emp where sal > (select avg(sal) from emp);
select语句中嵌套select语句,叫做子查询,后面会学。
group by:按照某个字段或者某些字段进行分组。
having:having是对分组之后的数据进行再次过滤。
案例:找出每个工作岗位的最高薪资。
select max(sal) from emp group by job;//先按照job分组,再找每组的最大薪资
select max(sal),job from emp group by job;
+----------+-----------+
| max(sal) | job |
+----------+-----------+
| 3000.00 | ANALYST |
| 1300.00 | CLERK |
| 2975.00 | MANAGER |
| 5000.00 | PRESIDENT |
| 1600.00 | SALESMAN |
+----------+-----------+
注意:分组函数一般都会和group by联合使用,这也是为什么它被称为分组函数的原因。
并且任何一个分组函数(count sum avg max min)都是在group by语句执行结束之后才会执行的。
当一条sql语句没有group by的话,整张表的数据会自成一组。
这条语句对吗:select ename,max(sal),job from emp group by job;
以上在mysql当中,查询结果是有的,但是结果没有意义。
在Oracle数据库当中会报错。语法错误。因为Oracle的语法规则比MySQL语法规则严谨。
所以记住一个规则:
当一条语句中有group by的话,select后面只能跟分组函数和按照该字段分组的字段。
select max(sal),job from emp group by job;//正确
select ename,max(sal),job from emp group by job;//并没有按照ename分组,所以不能写ename
每个工作岗位的平均薪资?
select job,avg(sal) from emp group by job;
+-----------+-------------+
| job | avg(sal) |
+-----------+-------------+
| ANALYST | 3000.000000 |
| CLERK | 1037.500000 |
| MANAGER | 2758.333333 |
| PRESIDENT | 5000.000000 |
| SALESMAN | 1400.000000 |
+-----------+-------------+
多个字段能不能联合起来一块分组?能
案例:找出每个部门不同工作岗位的最高薪资。
原表:select deptno,job,sal from emp order by deptno;
+--------+-----------+---------+
| deptno | job | sal |
+--------+-----------+---------+
| 10 | MANAGER | 2450.00 |
| 10 | PRESIDENT | 5000.00 |
| 10 | CLERK | 1300.00 |
| 20 | CLERK | 800.00 |
| 20 | MANAGER | 2975.00 |
| 20 | ANALYST | 3000.00 |
| 20 | CLERK | 1100.00 |
| 20 | ANALYST | 3000.00 |
| 30 | SALESMAN | 1600.00 |
| 30 | SALESMAN | 1250.00 |
| 30 | SALESMAN | 1250.00 |
| 30 | MANAGER | 2850.00 |
| 30 | SALESMAN | 1500.00 |
| 30 | CLERK | 950.00 |
+--------+-----------+---------+
案例:找出每个部门不同工作岗位的最高薪资。
select
deptno,job,max(sal)
from
emp
group by
deptno,job;
+--------+-----------+----------+
| deptno | job | max(sal) |
+--------+-----------+----------+
| 20 | CLERK | 1100.00 |
| 30 | SALESMAN | 1600.00 |
| 20 | MANAGER | 2975.00 |
| 30 | MANAGER | 2850.00 |
| 10 | MANAGER | 2450.00 |
| 20 | ANALYST | 3000.00 |
| 10 | PRESIDENT | 5000.00 |
| 30 | CLERK | 950.00 |
| 10 | CLERK | 1300.00 |
+--------+-----------+----------+
找出每个部门的最高薪资,要求显示薪资大于2900的数据。
第一步:找出每个部门的最高薪资
select max(sal),deptno from emp group by deptno;
+----------+--------+
| max(sal) | deptno |
+----------+--------+
| 5000.00 | 10 |
| 3000.00 | 20 |
| 2850.00 | 30 |
+----------+--------+
第二步:找出薪资大于2900
//这种方式效率低:
select max(sal),deptno from emp group by deptno having max(sal) > 2900;
+----------+--------+
| max(sal) | deptno |
+----------+--------+
| 5000.00 | 10 |
| 3000.00 | 20 |
+----------+--------+
//效率较高,建议能够使用where过滤的尽量使用where:
select max(sal),deptno from emp where sal > 2900 group by deptno;
+----------+--------+
| max(sal) | deptno |
+----------+--------+
| 5000.00 | 10 |
| 3000.00 | 20 |
+----------+--------+
找出每个部门的平均薪资,要求显示薪资大于2000的数据。
第一步:找出每个部门的平均薪资
select deptno,avg(sal) from emp group by deptno;
+--------+-------------+
| deptno | avg(sal) |
+--------+-------------+
| 10 | 2916.666667 |
| 20 | 2175.000000 |
| 30 | 1566.666667 |
+--------+-------------+
第二步:要求显示薪资大于2000的数据
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2000;
+--------+-------------+
| deptno | avg(sal) |
+--------+-------------+
| 10 | 2916.666667 |
| 20 | 2175.000000 |
+--------+-------------+
where后面不能使用分组函数:
select deptno,avg(sal) from emp where avg(sal) > 2000 group by deptno;//错误了。
这种情况只能使用having过滤。
书写顺序 执行顺序
select 5
..
from 1
..
where 2
..
group by 3
..
having 4
..
order by 6
..
from+表名称 从哪个表中查询
where+条件 条件查询 分组函数不可直接使用在where子句当中(因为使用分组函数要先分组group by)
group by+字段名 按照哪个字段进行分组
having+条件 分组之后再进行过滤(条件查询)能用where就用where,where不行再用having
having不能单独使用,只能和group by一起使用
select+要显示的内容
order by+字段名+asc升序/desc降序 按照哪个字段排序(默认升序)
分组函数(count sum avg max min)都是在group by语句执行结束之后才会执行的。
分组函数一般都会和group by联合使用,这也是为什么它被称为分组函数的原因。
当一条sql语句没有group by的话,整张表的数据会自成一组。
mysql> select distinct job from emp; // distinct关键字去除重复记录。
+-----------+
| job |
+-----------+
| CLERK |
| SALESMAN |
| MANAGER |
| ANALYST |
| PRESIDENT |
+-----------+
mysql> select ename,distinct job from emp;
以上的sql语句是错误的。因为ename有14条,而distinct job只有5条。
注意:distinct只能出现在所有字段的最前面。
注意:distinct出现在所有字段的最前方,代表所有字段联合起来去除重复记录。
原表:select deptno,job from emp order by deptno;
+--------+-----------+
| deptno | job |
+--------+-----------+
| 10 | MANAGER |
| 10 | PRESIDENT |
| 10 | CLERK |
| 20 | CLERK |
| 20 | MANAGER |
| 20 | ANALYST |
| 20 | CLERK |
| 20 | ANALYST |
| 30 | SALESMAN |
| 30 | SALESMAN |
| 30 | SALESMAN |
| 30 | MANAGER |
| 30 | SALESMAN |
| 30 | CLERK |
+--------+-----------+
mysql> select distinct deptno,job from emp;
+--------+-----------+
| deptno | job |
+--------+-----------+
| 20 | CLERK |
| 30 | SALESMAN |
| 20 | MANAGER |
| 30 | MANAGER |
| 10 | MANAGER |
| 20 | ANALYST |
| 10 | PRESIDENT |
| 30 | CLERK |
| 10 | CLERK |
+--------+-----------+
案例:统计岗位的数量?
select count(distinct job) from emp;
+---------------------+
| count(distinct job) |
+---------------------+
| 5 |
+---------------------+
在实际开发中,大部分的情况下都不是从单表中查询数据,一般都是多张表联合查询取出最终的结果。
在实际开发中,一般一个业务都会对应多张表,比如:学生和班级,起码两张表。
stuno stuname classno classname
-----------------------------------------------------------------------------------
1 zs 1 北京大兴区亦庄经济技术开发区第二中学高三1班
2 ls 1 北京大兴区亦庄经济技术开发区第二中学高三1班
...
如果学生和班级信息存储到一张表中,结果就像上面一样,数据会存在大量的重复,导致数据的冗余。
根据语法出现的年代来划分的话,包括:
SQL92(一些老的DBA可能还在使用这种语法。DBA:DataBase Administrator,数据库管理员)
SQL99(比较新的语法)
根据表的连接方式来划分,包括:
内连接:
等值连接
非等值连接
自连接
外连接:
左外连接(左连接)
右外连接(右连接)
全连接(这个不讲,很少用!)
在表的连接查询方面有一种现象被称为:笛卡尔积现象(笛卡尔乘积现象)
案例:找出每一个员工的部门名称,要求显示员工名和部门名。
emp表中只有员工名和部门编号,部门名在dept表中。
EMP表
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
DEPT表
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
select ename,dname from emp,dept;
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | OPERATIONS |
| SMITH | SALES |
| SMITH | RESEARCH |
| SMITH | ACCOUNTING |
| ALLEN | OPERATIONS |
| ALLEN | SALES |
| ALLEN | RESEARCH |
| ALLEN | ACCOUNTING |
| WARD | OPERATIONS |
| WARD | SALES |
| WARD | RESEARCH |
| WARD | ACCOUNTING |
| JONES | OPERATIONS |
| JONES | SALES |
| JONES | RESEARCH |
| JONES | ACCOUNTING |
| MARTIN | OPERATIONS |
| MARTIN | SALES |
| MARTIN | RESEARCH |
| MARTIN | ACCOUNTING |
| BLAKE | OPERATIONS |
| BLAKE | SALES |
| BLAKE | RESEARCH |
| BLAKE | ACCOUNTING |
| CLARK | OPERATIONS |
| CLARK | SALES |
| CLARK | RESEARCH |
| CLARK | ACCOUNTING |
| SCOTT | OPERATIONS |
| SCOTT | SALES |
| SCOTT | RESEARCH |
| SCOTT | ACCOUNTING |
| KING | OPERATIONS |
| KING | SALES |
| KING | RESEARCH |
| KING | ACCOUNTING |
| TURNER | OPERATIONS |
| TURNER | SALES |
| TURNER | RESEARCH |
| TURNER | ACCOUNTING |
| ADAMS | OPERATIONS |
| ADAMS | SALES |
| ADAMS | RESEARCH |
| ADAMS | ACCOUNTING |
| JAMES | OPERATIONS |
| JAMES | SALES |
| JAMES | RESEARCH |
| JAMES | ACCOUNTING |
| FORD | OPERATIONS |
| FORD | SALES |
| FORD | RESEARCH |
| FORD | ACCOUNTING |
| MILLER | OPERATIONS |
| MILLER | SALES |
| MILLER | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
56 rows in set (0.00 sec)
连接查询原理:将第一张表的每一条数据与第二张表的每一条数据匹配。
笛卡尔积现象:当两张表进行连接查询的时候,没有任何条件进行限制,最终的查询结果条数是两张表记录条数的乘积。
关于表的别名:
select e.ename,d.dname from emp e,dept d;
表的别名有什么好处?
第一:执行效率高。
第二:可读性好。
两张表中可能会有相同的字段名,所以要给表起别名。
从此开始,开始给表写别名。
写法:先起别名from emp e,dept d;然后再e.ename,d.dname
如何避免笛卡尔积现象:加条件进行过滤。
思考:避免了笛卡尔积现象,会减少记录的匹配次数吗?
不会,次数还是56次。只不过显示的是有效记录。不会提高连接查询效率。
案例:找出每一个员工的部门名称,要求显示员工名和部门名。
select
e.ename,d.dname
from
emp e , dept d
where
e.deptno = d.deptno; //SQL92,以后不用。
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| JONES | RESEARCH |
| MARTIN | SALES |
| BLAKE | SALES |
| CLARK | ACCOUNTING |
| SCOTT | RESEARCH |
| KING | ACCOUNTING |
| TURNER | SALES |
| ADAMS | RESEARCH |
| JAMES | SALES |
| FORD | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.00 sec)
内连接和外连接的区别在内外连接讲完之后再讲。
内连接之等值连接:最大特点是:条件是等量关系。
案例:查询每个员工的部门名称,要求显示员工名和部门名。
SQL92:(太老,不用了)
select
e.ename,d.dname
from
emp e, dept d
where
e.deptno = d.deptno;
SQL99:(常用的)
select
e.ename,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno; //不同表之间的连接条件是等号,所以叫等值连接
SQL99语法结构更清晰一些:表的连接条件和后来的where条件分离了。
语法:
...
A表
(inner)join
B表
on
表连接条件
where
...
// inner可以省略的,带着inner目的是可读性好一些。
select
e.ename,d.dname
from
emp e
inner join
dept d
on
e.deptno = d.deptno;
+--------+------------+
| ename | dname |
+--------+------------+
| CLARK | ACCOUNTING |
| KING | ACCOUNTING |
| MILLER | ACCOUNTING |
| SMITH | RESEARCH |
| JONES | RESEARCH |
| SCOTT | RESEARCH |
| ADAMS | RESEARCH |
| FORD | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| MARTIN | SALES |
| BLAKE | SALES |
| TURNER | SALES |
| JAMES | SALES |
+--------+------------+
内连接之非等值连接:最大的特点是:连接条件中的关系是非等量关系。
案例:找出每个员工的工资等级,要求显示员工名、工资、工资等级。
员工名和工资在表emp中,工资等级在表salgrade中
EMP表
mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
SALGRADE表
mysql> select * from salgrade;
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
select
e.ename,e.sal,s.grade
from
emp e
inner join // inner可以省略
salgrade s
on
e.sal between s.losal and s.hisal; //不同表之间的连接条件是非等量关系所以叫非等值连接
+--------+---------+-------+
| ename | sal | grade |
+--------+---------+-------+
| SMITH | 800.00 | 1 |
| ALLEN | 1600.00 | 3 |
| WARD | 1250.00 | 2 |
| JONES | 2975.00 | 4 |
| MARTIN | 1250.00 | 2 |
| BLAKE | 2850.00 | 4 |
| CLARK | 2450.00 | 4 |
| SCOTT | 3000.00 | 4 |
| KING | 5000.00 | 5 |
| TURNER | 1500.00 | 3 |
| ADAMS | 1100.00 | 1 |
| JAMES | 950.00 | 1 |
| FORD | 3000.00 | 4 |
| MILLER | 1300.00 | 2 |
+--------+---------+-------+
自连接:最大的特点是:一张表看做两张表。自己连接自己。
案例:找出每个员工的上级领导,要求显示员工名和对应的领导名。
员工名和领导名都在表emp中。
将一个表看作两个表:emp a和emp b
mysql> select empno,ename,mgr from emp;
emp a 员工表
+-------+--------+------+
| empno | ename | mgr |
+-------+--------+------+
| 7369 | SMITH | 7902 |
| 7499 | ALLEN | 7698 |
| 7521 | WARD | 7698 |
| 7566 | JONES | 7839 |
| 7654 | MARTIN | 7698 |
| 7698 | BLAKE | 7839 |
| 7782 | CLARK | 7839 |
| 7788 | SCOTT | 7566 |
| 7839 | KING | NULL |
| 7844 | TURNER | 7698 |
| 7876 | ADAMS | 7788 |
| 7900 | JAMES | 7698 |
| 7902 | FORD | 7566 |
| 7934 | MILLER | 7782 |
+-------+--------+------+
emp b 领导表
+-------+--------+
| empno | ename |
+-------+--------+
| 7566 | JONES |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7788 | SCOTT |
| 7839 | KING |
| 7902 | FORD |
+-------+--------+
员工的领导编号 = 领导的员工编号
select
a.ename as '员工名',b.ename as '领导名'
from
emp a
inner join
emp b
on
a.mgr = b.empno;
+--------+--------+
| 员工名 | 领导名 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
什么是外连接,和内连接有什么区别?
内连接:
假设A和B表进行连接,使用内连接的话,凡是A表和B表能够匹配上的记录查询出来,这就是内连接。
AB两张表没有主副之分,两张表是平等的。
第一张表的某条数据与第二张表匹配不上,这条数据就会被舍去。
外连接:
假设A和B表进行连接,使用外连接的话,AB两张表中有一张表是主表,一张表是副表,主要查询主表中
的数据,捎带着查询副表,当副表中的数据没有和主表中的数据匹配上,副表自动模拟出NULL与之匹配。
外连接的分类?
左外连接(左连接):表示左边的这张表是主表。
右外连接(右连接):表示右边的这张表是主表。
任何一个左连接都有右连接的写法,任何一个右连接也会有对应的左连接的写法。
案例:找出每个员工的上级领导?(所有员工必须全部查询出来。)
emp a 员工表
+-------+--------+------+
| empno | ename | mgr |
+-------+--------+------+
| 7369 | SMITH | 7902 |
| 7499 | ALLEN | 7698 |
| 7521 | WARD | 7698 |
| 7566 | JONES | 7839 |
| 7654 | MARTIN | 7698 |
| 7698 | BLAKE | 7839 |
| 7782 | CLARK | 7839 |
| 7788 | SCOTT | 7566 |
| 7839 | KING | NULL |
| 7844 | TURNER | 7698 |
| 7876 | ADAMS | 7788 |
| 7900 | JAMES | 7698 |
| 7902 | FORD | 7566 |
| 7934 | MILLER | 7782 |
+-------+--------+------+
emp b 领导表
+-------+--------+
| empno | ename |
+-------+--------+
| 7566 | JONES |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7788 | SCOTT |
| 7839 | KING |
| 7902 | FORD |
+-------+--------+
内连接:
select
a.ename '员工', b.ename '领导'
from
emp a
join
emp b
on
a.mgr = b.empno;
//king这条数据没有领导被舍去了
+--------+--------+
| 员工 | 领导 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
13 rows in set (0.00 sec)
外连接:(左外连接/左连接)
select
a.ename '员工', b.ename '领导'
from
emp a
left (outer) join // outer是可以省略的。
emp b
on
a.mgr = b.empno;
//king这条数据匹配不上但未被舍去
+--------+--------+
| 员工 | 领导 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| KING | NULL |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
14 rows in set (0.00 sec)
//上述左连接写成右连接
外连接:(右外连接/右连接)
select
a.ename '员工', b.ename '领导'
from
emp b
right (outer) join // outer是可以省略的。
emp a
on
a.mgr = b.empno;
注意:inner和outer都可以省略,因为不是根据inner和outer来判断是否为内连接还是外连接,
而是根据有无left,right;有left或right表示是外连接,没有则是内连接。
以后外连接使用居多(不能让无法匹配的数据丢失)
外连接最重要的特点是:主表的数据无条件的全部查询出来。
案例:找出哪个部门没有员工?
EMP表
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
DEPT
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
select
d.*
from
emp e
right join
dept d
on
e.deptno = d.deptno
where
e.empno is null;
+--------+------------+--------+
| DEPTNO | DNAME | LOC |
+--------+------------+--------+
| 40 | OPERATIONS | BOSTON |
+--------+------------+--------+
案例:找出每一个员工的部门名称以及工资等级。
EMP e
+-------+--------+---------+--------+
| empno | ename | sal | deptno |
+-------+--------+---------+--------+
| 7369 | SMITH | 800.00 | 20 |
| 7499 | ALLEN | 1600.00 | 30 |
| 7521 | WARD | 1250.00 | 30 |
| 7566 | JONES | 2975.00 | 20 |
| 7654 | MARTIN | 1250.00 | 30 |
| 7698 | BLAKE | 2850.00 | 30 |
| 7782 | CLARK | 2450.00 | 10 |
| 7788 | SCOTT | 3000.00 | 20 |
| 7839 | KING | 5000.00 | 10 |
| 7844 | TURNER | 1500.00 | 30 |
| 7876 | ADAMS | 1100.00 | 20 |
| 7900 | JAMES | 950.00 | 30 |
| 7902 | FORD | 3000.00 | 20 |
| 7934 | MILLER | 1300.00 | 10 |
+-------+--------+---------+--------+
DEPT d
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
SALGRADE s
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
注意,解释一下:
....
A
join
B
join
C
on
...
或者
....
A
join
B
on
...
join
C
on
...
表示:A表和B表先进行表连接,连接之后A表继续和C表进行连接。
或者A和B连接的结果再和C连接。
select
e.ename,d.dname,s.grade
from
emp e
join
dept d
on
e.deptno = d.deptno
join
salgrade s
on
e.sal between s.losal and s.hisal;
+--------+------------+-------+
| ename | dname | grade |
+--------+------------+-------+
| SMITH | RESEARCH | 1 |
| ALLEN | SALES | 3 |
| WARD | SALES | 2 |
| JONES | RESEARCH | 4 |
| MARTIN | SALES | 2 |
| BLAKE | SALES | 4 |
| CLARK | ACCOUNTING | 4 |
| SCOTT | RESEARCH | 4 |
| KING | ACCOUNTING | 5 |
| TURNER | SALES | 3 |
| ADAMS | RESEARCH | 1 |
| JAMES | SALES | 1 |
| FORD | RESEARCH | 4 |
| MILLER | ACCOUNTING | 2 |
+--------+------------+-------+
案例:找出每一个员工的部门名称、工资等级、以及上级领导。
select
e.ename '员工',d.dname,s.grade,e1.ename '领导'
from
emp e
join
dept d
on
e.deptno = d.deptno
join
salgrade s
on
e.sal between s.losal and s.hisal
left join //这里用外连接,否则用内连接king数据会被删除(king的领导为NULL)
emp e1
on
e.mgr = e1.empno;
注意:什么时候用外连接(注意NULL情况)
+--------+------------+-------+-------+
| 员工 | dname | grade | 领导 |
+--------+------------+-------+-------+
| SMITH | RESEARCH | 1 | FORD |
| ALLEN | SALES | 3 | BLAKE |
| WARD | SALES | 2 | BLAKE |
| JONES | RESEARCH | 4 | KING |
| MARTIN | SALES | 2 | BLAKE |
| BLAKE | SALES | 4 | KING |
| CLARK | ACCOUNTING | 4 | KING |
| SCOTT | RESEARCH | 4 | JONES |
| KING | ACCOUNTING | 5 | NULL |
| TURNER | SALES | 3 | BLAKE |
| ADAMS | RESEARCH | 1 | SCOTT |
| JAMES | SALES | 1 | BLAKE |
| FORD | RESEARCH | 4 | JONES |
| MILLER | ACCOUNTING | 2 | CLARK |
+--------+------------+-------+-------+
一直都是在DOS命令窗口写,使用这个工具就可以不使用DOS了
并且会很简便,甚至不需要写sql语句。
但是先不要用,先学习sql怎么写。
什么是子查询?子查询都可以出现在哪里?
select语句当中嵌套select语句,被嵌套的select语句是子查询。
子查询可以出现在哪里?
select
..(select).
from
..(select).
where
..(select).
案例:找出高于平均薪资的员工信息。
select * from emp where sal > avg(sal); //错误的写法,where后面不能直接使用分组函数。
第一步:找出平均薪资
select avg(sal) from emp;
+-------------+
| avg(sal) |
+-------------+
| 2073.214286 |
+-------------+
第二步:where过滤
select * from emp where sal > 2073.214286;
+-------+-------+-----------+------+------------+---------+------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+-------+-----------+------+------------+---------+------+--------+
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
+-------+-------+-----------+------+------------+---------+------+--------+
第一步和第二步合并:
select * from emp where sal > (select avg(sal) from emp);
案例:找出每个部门平均薪水的等级。
第一步:找出每个部门平均薪水(按照部门编号分组,求sal的平均值)
select deptno,avg(sal) as avgsal from emp group by deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 10 | 2916.666667 |
| 20 | 2175.000000 |
| 30 | 1566.666667 |
+--------+-------------+
第二步:将以上的查询结果当做临时表t,让t表和salgrade s表连接,条件是:t.avgsal between s.losal and s.hisal
两步合一步:
select
t.*,s.grade
from
(select deptno,avg(sal) as avgsal from emp group by deptno) t
join
salgrade s
on
t.avgsal between s.losal and s.hisal;
+--------+-------------+-------+
| deptno | avgsal | grade |
+--------+-------------+-------+
| 30 | 1566.666667 | 3 |
| 10 | 2916.666667 | 4 |
| 20 | 2175.000000 | 4 |
+--------+-------------+-------+
案例:找出每个部门平均的薪水等级。
第一步:找出每个员工的薪水等级。
select e.ename,e.sal,e.deptno,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal;
+--------+---------+--------+-------+
| ename | sal | deptno | grade |
+--------+---------+--------+-------+
| SMITH | 800.00 | 20 | 1 |
| ALLEN | 1600.00 | 30 | 3 |
| WARD | 1250.00 | 30 | 2 |
| JONES | 2975.00 | 20 | 4 |
| MARTIN | 1250.00 | 30 | 2 |
| BLAKE | 2850.00 | 30 | 4 |
| CLARK | 2450.00 | 10 | 4 |
| SCOTT | 3000.00 | 20 | 4 |
| KING | 5000.00 | 10 | 5 |
| TURNER | 1500.00 | 30 | 3 |
| ADAMS | 1100.00 | 20 | 1 |
| JAMES | 950.00 | 30 | 1 |
| FORD | 3000.00 | 20 | 4 |
| MILLER | 1300.00 | 10 | 2 |
+--------+---------+--------+-------+
第二步:基于以上结果,继续按照deptno分组,求grade平均值。//不用把上表当成临时表
from把两表连接成一表,然后按照e.deptno分组,然后显示编号和平均值
select
e.deptno,avg(s.grade)
from
emp e
join
salgrade s
on
e.sal between s.losal and s.hisal
group by
e.deptno;
+--------+--------------+
| deptno | avg(s.grade) |
+--------+--------------+
| 10 | 3.6667 |
| 20 | 2.8000 |
| 30 | 2.5000 |
+--------+--------------+
可以把第一步得到的表作为临时表继续按照deptno分组,求grade平均值,但没必要。
select
t.deptno,avg(t.grade)
from
(select e.deptno,e.sal,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal) t
group by
t.deptno;
+--------+--------------+
| deptno | avg(t.grade) |
+--------+--------------+
| 20 | 2.8000 |
| 30 | 2.5000 |
| 10 | 3.6667 |
+--------+--------------+
案例:找出每个员工所在的部门名称,要求显示员工名和部门名。
select
e.ename,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
select
e.ename,(select d.dname from dept d where e.deptno = d.deptno) as dname
from
emp e;
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| JONES | RESEARCH |
| MARTIN | SALES |
| BLAKE | SALES |
| CLARK | ACCOUNTING |
| SCOTT | RESEARCH |
| KING | ACCOUNTING |
| TURNER | SALES |
| ADAMS | RESEARCH |
| JAMES | SALES |
| FORD | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
案例:找出工作岗位是SALESMAN和MANAGER的员工?
第一种:select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
第二种:select ename,job from emp where job in('MANAGER','SALESMAN');
+--------+----------+
| ename | job |
+--------+----------+
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| JONES | MANAGER |
| MARTIN | SALESMAN |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| TURNER | SALESMAN |
+--------+----------+
第三种:union
select ename,job from emp where job = 'MANAGER'
union
select ename,job from emp where job = 'SALESMAN';
+--------+----------+
| ename | job |
+--------+----------+
| JONES | MANAGER |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| MARTIN | SALESMAN |
| TURNER | SALESMAN |
+--------+----------+
两张不相干的表中的数据拼接在一起显示?
select ename from emp
union
select dname from dept;
+------------+
| ename |
+------------+
| SMITH |
| ALLEN |
| WARD |
| JONES |
| MARTIN |
| BLAKE |
| CLARK |
| SCOTT |
| KING |
| TURNER |
| ADAMS |
| JAMES |
| FORD |
| MILLER |
| ACCOUNTING | 从此开始是部门名
| RESEARCH |
| SALES |
| OPERATIONS |
+------------+
mysql> select ename,sal from emp
-> union
-> select dname from dept; //拼接的数据必须两两相对应。不能一张表是一个数据,另一张表是两个数据,这样无法拼接!
ERROR 1222 (21000): The used SELECT statements have a different number of columns
5.1、limit是mysql特有的,其他数据库中没有,不通用。
(Oracle中有一个相同的机制,叫做rownum)
5.2、limit取结果集中的部分数据,这是它的作用。
5.3、语法机制:
limit startIndex,length
startIndex表示起始位置,从0开始,0表示第一条数据。
length表示取几个
案例:取出工资前5名的员工(思路:降序取前5个。先排序,再去前5个)
select ename,sal from emp order by sal desc;
+--------+---------+
| ename | sal |
+--------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
| WARD | 1250.00 |
| MARTIN | 1250.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| SMITH | 800.00 |
+--------+---------+
取前5个:
select ename,sal from emp order by sal desc limit 0, 5;
select ename,sal from emp order by sal desc limit 5;//startIndex默认是0
+-------+---------+
| ename | sal |
+-------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+
5.4、limit是sql语句最后执行的一个环节:
select 5
...
from 1
...
where 2
...
group by 3
...
having 4
...
order by 6
...
limit 7
...;
5.5、案例:找出工资排名在第4到第9名的员工?
select ename,sal from emp order by sal desc limit 3,6;
+--------+---------+
| ename | sal |
+--------+---------+
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
+--------+---------+
5.6、通用的标准分页sql?
不可能把所有的记录都放在一页上,所以要分页。
每页显示3条记录:
第1页:0, 3
第2页:3, 3
第3页:6, 3
第4页:9, 3
第5页:12, 3
每页显示pageSize条记录:
第pageNo页:(pageNo - 1) * pageSize, pageSize
pageSize是什么?是每页显示多少条记录
pageNo是什么?显示第几页
java代码{
int pageNo = 2; // 页码是2
int pageSize = 10; // 每页显示10条
limit (pageNo - 1) * pageSize, pageSize
}
建表语句的语法格式:
create table 表名(
字段名1 数据类型 约束,
字段名2 数据类型 约束,
字段名3 数据类型 约束,
....
); //先不管约束
关于MySQL当中字段的数据类型?以下只说常见的
int 整数型(java中的int)
bigint 长整型(java中的long)
float 浮点型(java中的float double)
char 定长字符串(java中的String)
varchar 可变长字符串 至多255个字符(java中的StringBuffer/StringBuilder)
date 日期类型 可以直接存储日期(对应Java中的java.sql.Date类型)
BLOB 二进制大对象(存储图片、视频等流媒体信息) Binary Large OBject (对应java中的Object)
CLOB 字符大对象(存储较大文本,比如,可以存储4G的字符串) Character Large OBject(对应java中的Object)
......
char和varchar怎么选择?
比如:有一个字段name
若数据类型是char(6),表示字符串的大小不超过6,超过6报错。
若数据类型是varchar(6),如果name是jack,则只分配4个空间,而char(6)会分配6个空间。
即不管name是几个字符,在不超过6的条件下,char(6)都会分配6个空间,varchar(6)根据字符个数分配空间。
根据实际情况选char/varchar
char类型效率高,不用去判断分配几个空间。
在实际的开发中,当某个字段中的数据长度不发生改变的时候,是定长的,例如:性别、生日等都是采用char。
当一个字段的数据长度不确定,例如:简介、姓名等都是采用varchar。
BLOB和CLOB类型的使用?
电影表: t_movie
id(int) name(varchar) playtime(date/char) haibao(BLOB) history(CLOB)
----------------------------------------------------------------------------------------
1 蜘蛛侠
2
3
表名在数据库当中一般建议以:t_或者tbl_开始。
创建学生表:
学生信息包括:
学号、姓名、性别、班级编号、生日
学号:bigint
姓名:varchar
性别:char
班级编号:int
生日:char
create table t_student(
no bigint,
name varchar(255),
sex char(1),
classno varchar(255),
birth char(10)
);
语法格式:
insert into 表名(字段名1,字段名2,字段名3,....) values(值1,值2,值3,....);
要求:字段的数量和值的数量相同,并且数据类型要对应相同。
//DOS命令窗口字体是GBK,而输入的是utf-8,所以输入中文会不匹配乱码,先不输中文。
insert into t_student(no,name,sex,classno,birth) values(1,'zhangsan','1','gaosan1ban');//错误
ERROR 1136 (21S01): Column count doesn't match value count at row 1
insert into t_student(no,name,sex,classno,birth) values(1,'zhangsan','1','gaosan1ban', '1950-10-12');
mysql> select * from t_student;
+------+----------+------+------------+------------+
| no | name | sex | classno | birth |
+------+----------+------+------------+------------+
| 1 | zhangsan | 1 | gaosan1ban | 1950-10-12 |
+------+----------+------+------------+------------+
insert into t_student(name,sex,classno,birth,no) values('lisi','1','gaosan1ban', '1950-10-12',2);
mysql> select * from t_student;
+------+----------+------+------------+------------+
| no | name | sex | classno | birth |
+------+----------+------+------------+------------+
| 1 | zhangsan | 1 | gaosan1ban | 1950-10-12 |
| 2 | lisi | 1 | gaosan1ban | 1950-10-12 |
+------+----------+------+------------+------------+
insert into t_student(name) values('wangwu'); // 除name字段之外,剩下的所有字段自动插入NULL。
mysql> select * from t_student;
+------+----------+------+------------+------------+
| no | name | sex | classno | birth |
+------+----------+------+------------+------------+
| 1 | zhangsan | 1 | gaosan1ban | 1950-10-12 |
| 2 | lisi | 1 | gaosan1ban | 1950-10-12 |
| NULL | wangwu | NULL | NULL | NULL |
+------+----------+------+------------+------------+
//insert语句每次都会插入一条新的数据,而要修改某条数据的某个字段用update
insert into t_student(no) values(3);
mysql> select * from t_student;
+------+----------+------+------------+------------+
| no | name | sex | classno | birth |
+------+----------+------+------------+------------+
| 1 | zhangsan | 1 | gaosan1ban | 1950-10-12 |
| 2 | lisi | 1 | gaosan1ban | 1950-10-12 |
| NULL | wangwu | NULL | NULL | NULL |
| 3 | NULL | NULL | NULL | NULL |
+------+----------+------+------------+------------+
//当这个表存在的话删除,删除某个表:
drop table if exists t_student;
//再新建这个表
create table t_student(
no bigint,
name varchar(255),
sex char(1) default 1, //加上default 1,默认为1,不写这句,默认是NULL
classno varchar(255),
birth char(10)
);
//可以使用desc t_student; 查看default是什么
insert into t_student(name) values('zhangsan');
mysql> select * from t_student;
+------+----------+------+---------+-------+
| no | name | sex | classno | birth |
+------+----------+------+---------+-------+
| NULL | zhangsan | 1 | NULL | NULL |
+------+----------+------+---------+-------+
需要注意的地方:
当一条insert语句执行成功之后,表格当中必然会多一行记录。
即使多的这一行记录当中某些字段是NULL,后期也没有办法再执行
insert语句插入数据了,只能使用update进行更新。
// 字段可以省略不写,但是后面的value对数量和顺序都有要求(数量和顺序都不能错)
insert into t_student values(1,'jack','0','gaosan2ban','1986-10-23');
mysql> select * from t_student;
+------+----------+------+------------+------------+
| no | name | sex | classno | birth |
+------+----------+------+------------+------------+
| NULL | zhangsan | 1 | NULL | NULL |
| 1 | jack | 0 | gaosan2ban | 1986-10-23 |
+------+----------+------+------------+------------+
insert into t_student values(1,'jack','0','gaosan2ban');
ERROR 1136 (21S01): Column count doesn't match value count at row 1
// 一次插入多行数据
insert into t_student
(no,name,sex,classno,birth)
values
(3,'rose','1','gaosi2ban','1952-12-14'),(4,'laotie','1','gaosi2ban','1955-12-14');
mysql> select * from t_student;
+------+----------+------+------------+------------+
| no | name | sex | classno | birth |
+------+----------+------+------------+------------+
| NULL | zhangsan | 1 | NULL | NULL |
| 1 | jack | 0 | gaosan2ban | 1986-10-23 |
| 3 | rose | 1 | gaosi2ban | 1952-12-14 |
| 4 | laotie | 1 | gaosi2ban | 1955-12-14 |
+------+----------+------+------------+------------+
//当这个表存在的话删除,删除某个表:
drop table if exists t_student;
语法:
drop table 表名; //通用写法
drop table if exits 表名; //Oracle中不支持这种写法
语法:
create table 表名 as select语句;
将查询结果当做表创建出来。
例子:create table emp1 as select * from emp;
创建一个表emp1,将select的查询结果(select * from emp)作为一个新表创建出来。
创建一个新表:mysql> create table dept1 as select * from dept;
mysql> select * from dept1;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
mysql> insert into dept1 select * from dept; //将查询结果当作数据插入表中
mysql> select * from dept1;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
注意:dept中的字段个数要等于dept1中的字段个数,才能插入。
语法格式:
update 表名 set 字段名1=值1,字段名2=值2... where 条件;
注意:没有条件整张表数据全部更新。
mysql> select * from dept1;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
案例:在表dept1中,将部门10的LOC修改为SHANGHAI,将部门名称修改为RENSHIBU
update dept1 set loc = 'SHANGHAI', dname = 'RENSHIBU' where deptno = 10;
mysql> select * from dept1;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | RENSHIBU | SHANGHAI |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
| 10 | RENSHIBU | SHANGHAI |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
更新所有记录
update dept1 set loc = 'x', dname = 'y';
mysql> select * from dept1;
+--------+-------+------+
| DEPTNO | DNAME | LOC |
+--------+-------+------+
| 10 | y | x |
| 20 | y | x |
| 30 | y | x |
| 40 | y | x |
| 10 | y | x |
| 20 | y | x |
| 30 | y | x |
| 40 | y | x |
+--------+-------+------+
语法格式:
delete from 表名 where 条件;
注意:没有条件全部删除。
删除10部门数据?
delete from dept1 where deptno = 10;
删除所有记录?
delete from dept1;
怎么删除大表中的数据?(重点)
truncate table 表名; // 表被截断,不可回滚。永久丢失。
//使用delete删除数据后,数据还可以恢复,即回滚;使用truncate删除后无法恢复数据。
drop、truncate和delete的区别:
百度吧。简单来说:truncate和delete只删除表中数据,而drop删除表的数据和表结构。
DQL(select) 查询数据
DML(insert delete update) 对于 表中的数据 进行 增删改
DDL(create drop alter) 对于 表结构 进行 创建 删除 修改
除了alter,其他以上都学了。
对于表结构的修改,即alter就不讲了,使用工具来做。
对于表结构的修改,这里不讲了,大家使用工具完成即可。因为在实际开发中表一旦
设计好之后,对表结构的修改是很少的,修改表结构就是对之前的设计进行了否定,即使
需要修改表结构,我们也可以直接使用工具操作。修改表结构的语句不会出现在Java代码当中。
出现在java代码当中的sql包括:insert delete update select(这些都是表中的数据操作)
增删改查有一个术语:CRUD操作
Create(增) Retrieve(检索) Update(修改) Delete(删除)
在创建表的时候,可以给表的字段添加相应的约束,添加约束的目的是为了保证表中数据的
合法性、有效性、完整性。
常见的约束有哪些呢?
非空约束(not null):约束的字段不能为NULL
唯一约束(unique):约束的字段不能重复
主键约束(primary key):约束的字段既不能为NULL,也不能重复(简称PK)
外键约束(foreign key):...说不太清楚(简称FK)
检查约束(check):注意Oracle数据库有check约束,但是mysql没有,目前mysql不支持该约束。
drop table if exists t_user;
create table t_user(
id int,
username varchar(255) not null,
password varchar(255)
);
//编译错误,约束username字段不能为空!
insert into t_user(id,password) values(1,'123');
ERROR 1364 (HY000): Field 'username' doesn't have a default value
mysql>desc t_user;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| username | varchar(255) | NO | | NULL | |
| password | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+-------+
insert into t_user(id,username,password) values(1,'lisi','123');
* 唯一性约束修饰的字段具有唯一性,不能重复。但可以为null。
* 案例:给某一列添加unique
drop table if exists t_user;
create table t_user(
id int,
username varchar(255) unique //【列级约束】
);
insert into t_user values(1,'zhangsan');
insert into t_user values(2,'zhangsan');
ERROR 1062 (23000) : Duplicate entry 'zhangsan' for key 'username'
//出现编译错误,唯一性约束,该字段与上一行字段重复,但可以为null(如下三行语句)
insert into t_user(id) values(2);
insert into t_user(id) values(3);
insert into t_user(id) values(4);
*案例:给两个列或者多个列添加unique
drop table if exists t_user;
create table t_user(
id int,
usercode varchar(255),
username varchar(255),
unique(usercode,username) //多个字段联合起来添加一个约束unique 【表级约束】
);
insert into t_user values(1,'111','zs');
insert into t_user values(2,'111','ls');
insert into t_user values(3,'222','zs');
select * from t_user;
+------+----------+----------+
| id | usercode | username |
+------+----------+----------+
| 1 | 111 | zs |
| 2 | 111 | ls |
| 3 | 222 | zs |
+------+----------+----------+
//出现编译错误!这两个字段联合起来不能一样
insert into t_user values(4,'111','zs');
ERROR 1062 (23000) : Duplicate entry '111-zs' for key 'usercode'
* drop table if exists t_user;
create table t_suer(
id int,
usercode varchar(255) unique, //【列级约束】
username varchar(255) unique //【列级约束】
);
//'111'重复了
insert into t_user values(1,'111','zs');
insert into t_user values(2,'111','ls');
ERROR 1062 (23000) : Duplicate entry '111' for key 'usercode'
* 注意:第三个案例和第二个案例的区别。即列级约束和表级约束的不同。
列级约束可以写成表级约束:比如第一个案例可以成unique(username)
* 列级约束和表级约束的区别就是写法不同:
列级约束:直接写在某一字段后如:username varchar(255) unique
表级约束:不写在某一字段后,另起一行写如:unique(username)
和表示的意思不同:
列级约束:
usercode varchar(255) unique
username varchar(255) unique
表示usercode和usercode之间不同,username和username之间不同
例如:(1,'111','zs');
(2,'111','lisi');//错误
表级约束:
unique(usercode,username)
表示usercode1和username1整体不能等于usercode2和username2
(usercode1,username1)!=(usercode2,username2)
例如:(1,'111','zs');//对
(2,'111','lisi');//对
(3,'111','zs');//错
* 注意:not null约束只有列级约束,没有表级约束。
* 怎么给一张表添加主键约束呢?
drop table if exists t_user;
create table t_user(
id int primary key, //【列级约束】
username varchar(255),
email varchar(255)
);
insert into t_user(id,username,email) values(1,'zs','[email protected]');
insert into t_user(id,username,email) values(2,'ls','[email protected]');
insert into t_user(id,username,email) values(3,'ww','[email protected]');
select * from t_user;
+-----------------------------+
| id | username | email |
+-----------------------------+
| 1 | zs | [email protected] |
| 2 | ls | [email protected] |
| 3 | ww | [email protected] |
+----+----------+-------------+
//出现编译错误,主键约束,不能为null也不能重复!//重复
insert into t_user(id,username,email) values(1,'jack','[email protected]');
ERROR 1364 (HY000) : Field 'id' doesn't have a default value
//出现编译错误,主键约束,不能为null也不能重复!//空
insert into t_user(username,email) values('jack','[email protected]');
ERROR 1364 (HY000): Field 'id' doesn't have a default value
根据以上的测试得出:
id是主键,因为添加了主键约束,主键字段中的数据不能为null,也不能重复。
主键的特点:不能为null,也不能重复。
* 主键相关的术语?
主键约束 :primary key
主键字段 : id字段添加primary key之后,id叫做主键字段
主键值 :id字段中的每一个值都是主键值。
* 主键有什么作用?
-表的设计三范式中有要求,第一范式就要求任何一张表都应该有主键。
-主键的作用:
主键值是这行记录在这张表上的唯一标识,用来区分每一条数据。(就像是身份证号)
* 主键的分类:
-根据主键字段的字段数量来划分:
单一主键:推荐的,常用的。(只有一个字段是主键约束)
复合主键:多个字段联合起来添加一个主键约束
(复合主键不建议使用,因为复合主键违背三范式)
-根据主键性质来划分:
自然主键(这种方式是推荐的):
主键值最好就是一个和业务没有任何关系的自然数。
业务主键(不推荐使用):
主键值和系统的业务挂钩,
例如:拿着银行卡的卡号做主键、拿着身份证号做为主键。
最好不要拿着和业务挂钩的字段做为主键。
因为以后的业务一旦发生改变的时候,主键也可能需要随着
发生变化,但有的时候没有办法变化,因为变化可能会导致主键重复。
* 一张表的主键约束只能有1个(必须记住)
(但是可以多个字段联合起来添加一个主键约束,即复合主键,但不建议使用)
* 使用表级约束方式定义主键:
drop table if exists t_user;
create table t_user(
id int,
username varchar(255),
primary key(id) //单一主键 表级约束方式
);
insert into t_user(id,username) values(1,'zs');
insert into t_user(id,username) values(2,'ls');
insert into t_user(id,username) values(3,'ws');
insert into t_user(id,username) values(4,'cs');
select * from t_user;
insert into t_user(id,username) values(3,'cx'); //出现编译错误!
ERROR 1062 (23000) : Duplicate entry '4' for key 'PRIMARY'
以下内容是演示一下复合主键,不需要掌握:
drop table if exists t_user;
create table t_user(
id int,
username varchar(255),
password varchar(255),
primary key(id,username) //复合主键 表级约束方式
);
insert ......
* mysql提供主键值自增:(非常重要)
drop table if exists t_user;
create table t_user(
//id字段自动维护一个自增的数字,从1开始,以1递增。
id int primary key auto_increment,
username varchar(255)
);
insert into t_user(username) values('a');
insert into t_user(username) values('b');
insert into t_user(username) values('c');
insert into t_user(username) values('d');
insert into t_user(username) values('e');
insert into t_user(username) values('f');
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
| 6 | f |
+----+----------+
注意:主键应该系统自动生成,所以添加auto_increment,自增可以使用工具。
提示:Oracle当中也提供了一个自增机制,叫做:序列(sequence)对象。
* 关于外键约束的相关术语:
外键约束:foreign key
外键字段:添加有外键约束的字段
外键值:外键字段中的每一个值。
* 业务背景:
请设计数据库表,用来维护学生和班级的信息?
第一种方案:一张表存储所有数据
缺点:冗余【不推荐】
no(pk) name classno classname
-----------------------------------------------------------
1 zs1 101 河南省平顶山市舞钢市垭口一高高三1班
2 zs2 101 河南省平顶山市舞钢市垭口一高高三1班
3 zs3 102 河南省平顶山市舞钢市垭口一高高三2班
4 zs4 102 河南省平顶山市舞钢市垭口一高高三2班
5 zs5 102 河南省平顶山市舞钢市垭口一高高三2班
第二种方案:两张表(班级表和学生表)
t_class 班级表
cno(pk) cname
-------------------------------------------------------------
101 河南省平顶山市舞钢市垭口一高高三1班
102 河南省平顶山市舞钢市垭口一高高三2班
t_student 学生表
sno(pk) sname classno(该字段添加外键约束fk)
-----------------------------------------------------------
1 zs1 101
2 zs2 101
3 zs3 102
4 zs4 102
5 zs5 102
注意:
classno字段加了外键约束fk引用cno字段,则classno的值必须是cno里有的值
否则报错。若没加fk,则classno的值随便。
t_student中的classno字段引用t_class表中的cno字段
此时t_student表叫做子表,t_class表叫做父表。
* 将以上表的建表语句写出来:
删除数据的时候,先删除子表,再删除父表。
添加数据的时候,先添加父表,再添加子表。
创建表的时候,先创建父表,再创建子表。
drop table if exists t_student;
drop table if exists t_class;
create table t_class(
cno int,
cname varchar(255),
primary key(cno) //主键约束
);
create table t_student(
sno int,
sname varchar(255),
classno int,
primary key(sno), //主键约束
foreign key(classno) references t_class(cno) //外键约束
);
//references 引用
//先插父表
insert into t_class values(101,'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
insert into t_class values(102,'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy');
//再插子表
insert into t_student values(1,'zs1',101);
insert into t_student values(2,'zs2',101);
insert into t_student values(3,'zs3',102);
insert into t_student values(4,'zs4',102);
insert into t_student values(5,'zs5',102);
insert into t_student values(6,'zs6',102);
select * from t_class;
+-----+-----------------------------------+
| cno | cname |
+-----+-----------------------------------+
| 101 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| 102 | yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy |
+-----+-----------------------------------+
select * from t_student;
+-----+-------+---------+
| sno | sname | classno |
+-----+-------+---------+
| 1 | zs1 | 101 |
| 2 | zs2 | 101 |
| 3 | zs3 | 102 |
| 4 | zs4 | 102 |
| 5 | zs5 | 102 |
| 6 | zs6 | 102 |
+-----+-------+---------+
//编译错误,引用的103,父表中cno字段没有该数据!
insert into t_student values(7,'lisi',103);
ERROR 1452 (23000) : Cannot add or update a child row :aforeign key constraint fails (bjpowernode INT YT......)
* 外键值可以为NULL?
外键可以为null。
insert into t_student(sno,sname) values(7,'zs7');
select * from t_student;
+-----+-------+---------+
| sno | sname | classno |
+-----+-------+---------+
| 1 | zs1 | 101 |
| 2 | zs2 | 101 |
| 3 | zs3 | 102 |
| 4 | zs4 | 102 |
| 5 | zs5 | 102 |
| 6 | zs6 | 102 |
| 7 | zs7 | NULL |
+-----+-------+---------+
* 外键字段引用其他表的某个字段的时候,被引用的字段必须是主键吗?
注意:被引用的字段不一定是主键,但至少是具有unique约束,具有唯一性,不可重复!
create table t_x(id int); //建表
show create table t_x; //查看建表语句
//这个才是真正的建表语句:
CREATE TABLE `t_x` (
`id` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
注意:在MySQL当中,凡是标识符使用飘号括起来的。最好别用,不通用。
建表的时候可以指定存储引擎,也可以指定字符集。
mysql默认使用的存储引擎是InnoDB方式。
默认采用的字符集是UTF-8。
存储引擎这个名字只有在mysql中存在。
(Oracle中有对应的机制,但不叫做存储引擎。Oracle中没有特殊的名字,就是"表的存储方式")
mysql支持很多存储引擎,每个存储引擎都对应了一种不同的存储方式。
每一个存储引擎都有自己的优缺点,需要在合适的时机选择合适的存储引擎。
show engines \G;
mysql 5.5.36版本支持的光速引擎有9个:
*************************** 1. row ***************************
Engine: FEDERATED
Support: NO
Comment: Federated MySQL storage engine
Transactions: NULL
XA: NULL
Savepoints: NULL
*************************** 2. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 3. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 4. row ***************************
Engine: BLACKHOLE
Support: YES
Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
XA: NO
Savepoints: NO
*************************** 5. row ***************************
Engine: CSV
Support: YES
Comment: CSV storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 6. row ***************************
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 7. row ***************************
Engine: ARCHIVE
Support: YES
Comment: Archive storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 8. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
*************************** 9. row ***************************
Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
XA: NO
Savepoints: NO
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO //MyISAM这种存储引擎不支持事务。
XA: NO
Savepoints: NO
MyISAM是mysql最常用的存储引擎,但是这种存储引擎不是默认的。
MyISAM采用三个文件组织一个表:
xxx.frm(存储格式的文件)
xxx.MYD(存储表中数据的文件)
xxx.MYI(存储表中索引的文件)
优点:可被压缩,节省存储空间。并且可以转换为只读表,提高检索效率。
缺点:不支持事务。
--------------------------------------------------------------------------
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
mysql默认使用的存储引擎是InnoDB方式。
优点:支持事务、行级锁、外键等。这种存储引擎数据的安全得到保障。
表的结构存储在xxx.frm文件中
数据存储在tablespace这样的表空间中(逻辑概念),无法被压缩,无法转换成只读。
这种InnoDB存储引擎在MySQL数据库崩溃之后提供自动恢复机制。
InoDB支持级联删除和级联更新。
----------------------------------------------------------------------------------
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
缺点:不支持事务。数据容易丢失。因为所有数据和索引都是存储在内存当中的。
优点:查询速度最快。
以前叫做HEPA引擎。
一个事务是一个完整的业务逻辑单元,不可再分。
比如:银行账户,从A账户向B账户转账10000元,需要执行两条update语句:
update t_act set balance = balance - 10000 where actno = 'act-001';
update t_act set balance = balance + 10000 where actno = 'act-002';
以上两条DML语句必须同时成功,或者同时失败,不允许出现一条成功,一条失败。
想要保证以上的两条DML语句同时成功或者同时失败,那么就要使用数据库的"事务机制"。
事务的主要语句:
commit 提交
rollback 回滚
为什么?因为他们这三个语句都是和数据库表当中的"数据"相关的。
事务的存在是为了保证数据的完整性,安全性。
不需要事务机制。
但实际情况不是这样的,通常一个"事儿(事务【业务】)"需要多条DML语句共同联合完成。
事务包括四大特性:ACID
A:原子性:事务是最小的工作单元,不可再分。
C:一致性:事务必须保证多条DML语句同时成功或者同时失败。
I:隔离性:事务A与事务B之间具有隔离。
D:持久性:持久性说的是最终数据必须持久化到硬盘中,事务才算成功结束。
事务1,事务2,事务1和事务2操作同一张员工表。
事务1操作员工表后事务2能否读到,事务2操作员工表后事务1能否读到
这就涉及到了事务之间的隔离性。(相当于多线程并发)
事务隔离性存在隔离级别,理论上隔离级别包括4个(实际上都从第二级别开始):
第一级别:读未提交(read uncommitted)(级别低)
对方事务还没有提交,我们当前事务可以读取到对方未提交的数据。
读未提交存在脏读(Dirty Read) 现象:表示读到了脏数据。
未提交,数据在缓存中还未在硬盘中,数据极其不稳定。
第二级别:读已提交(read committed)
对方事务提交之后的数据我方可以读取到。
读已提交存在的问题是:不可重复读
不可重复读(人家提交后我读了,人家改了再提交我就无法再读到第一次读到的数据了)
这种隔离级别解决了:脏读现象。
第三级别:可重复读(repeatable read)
可重复读(只能读到别人第一次提交的数据,无法读到别人修改后再提交的数据)
这种隔离级别解决了:不可重复读问题。
这种隔离级别存在的问题是:读取到的数据是幻象。
第四级别:序列化读/串行化读
解决了所有问题。
效率低,需要事务排队。
该级别:两个事务无法并发,事务1没结束时事务2无法开启。
//不懂就看下面对于两个事务的隔离的演示。
Oracle数据库默认的隔离级别是:第二级别,读已提交。
mysql数据库默认的隔离级别是:第三级别,可重复读。
* mysql事务默认情况下是自动提交的。
什么是自动提交?
只要执行任意一条DML语句则提交一次。
怎么关闭默认提交?
start transaction;
---------------------------------------------------------------
演示事务:
* 建表:
drop table if exists t_user;
create table t_user(
id int primary key auto_increment,
username varchar(255)
);
----------------------------------------------------------------
* 演示:mysql中的事务是支持自动提交的,只要执行一条DML语句,则提交一次。
mysql> insert into t_user(username) values('zs');
Querk OK, 1 row affected (0.03 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zs |
+----+----------+
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user; //如果不是自动提交,回滚之后为Empty
+----+----------+
| id | username |
+----+----------+
| 1 | zs |
+----+----------+
----------------------------------------------------------------
* 演示:使用start transaction; 关闭自动提交机制。
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zs |
+----+----------+
1 row in set (0.00 sec)
mysql> start transaction; //关闭自动提交机制
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t_user(username) values("lisi");
Query OK, 1 row affected (0.00 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zs |
| 2 | lisi |
+----+----------+
2 rows in set (0.00 sec)
mysql> insert into t_user(username) values("wangwu");
Query OK, 1 row affected (0.00 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zs |
| 2 | lisi |
| 3 | wangwu |
+----+----------+
3 rows in set (0.00 sec)
mysql> rollback; //回滚
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user; //关闭自动提交机制,且未再提交,回滚到上一次提交处
+----+----------+
| id | username |
+----+----------+
| 1 | zs |
+----+----------+
1 row in set (0.00 sec)
----------------------------------------------------------------
* 演示:使用start transaction; 关闭自动提交机制,再提交commit
//上一个事务rollback后,上一个事务就结束了,要重新关闭自动提交机制
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t_user(username) values("wangwu");
Query OK, 1 row affected (0.00 sec)
mysql> insert into t_user(username) values("object");
Query OK, 1 row affected (0.00 sec)
mysql> insert into t_user(username) values("joke");
Query OK, 1 row affected (0.00 sec)
mysql> insert into t_user(username) values("xiaozhaozhao");
Query OK, 1 row affected (0.00 sec)
mysql> select * from t_user;
+----+--------------+
| id | username |
+----+--------------+
| 1 | zs |
| 4 | wangwu |
| 5 | object |
| 6 | joke |
| 7 | xiaozhaozhao |
+----+--------------+
5 rows in set (0.00 sec)
mysql> commit; //手动提交
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user;
+----+--------------+
| id | username |
+----+--------------+
| 1 | zs |
| 4 | wangwu |
| 5 | object |
| 6 | joke |
| 7 | xiaozhaozhao |
+----+--------------+
5 rows in set (0.00 sec)
mysql> rollback; //手动提交后,再回滚不变
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user;
+----+--------------+
| id | username |
+----+--------------+
| 1 | zs |
| 4 | wangwu |
| 5 | object |
| 6 | joke |
| 7 | xiaozhaozhao |
+----+--------------+
5 rows in set (0.00 sec)
----------------------------------------------------------------
rollback:回滚(回到上一个提交点,此次事务结束)
commit:提交(该事务结束)
start transaction:关闭自动提交机制。
savepoint+名字1:保存该点此处,rollback+名字1,就回到了该保存点处。
----------------------------------------------------------------
* 使用两个事务,演示隔离级别:
使用两个事务,要开启两个DOS命令窗口,操作同一张表。
事务1,事务2,表1。事务1和事务2同时操作表1.
演示第1级别:读未提交(read uncommitted)
设置事务的全局隔离级别:
set global transaction isolation level read uncommitted;
查看事务的全局隔离级别:
select @@global.transaction_isolation;//mysql8.0+版本
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READ-UNCOMMITTED |
+--------------------------------+
现象:对方事务还没有提交,我们当前事务可以读取到对方未提交的数据。
事务1对表1增加1条数据,但未提交commit,但事务2可以查询到表1增加了1条数据。
演示第二级别;读已提交(read committed)
设置事务的全局隔离级别:
set global transaction isolation level read committed;
查看事务的全局隔离级别:
select @@global.transaction_isolation;//mysql8.0+版本
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READCOMMITTED |
+--------------------------------+
现象:读已提交
事务2对表1增加1条数据,事务2不提交,事务1查看不到新的数据
只有事务2提交之后,事务1才能查看到新数据。
演示第三级别:可重复读(repeatable read)
设置事务的全局隔离级别:
set global transaction isolation level repeatable read;
查看事务的全局隔离级别:
select @@global.transaction_isolation;//mysql8.0+版本
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
现象:可重复读
事务1对表1增加了1条数据,然后提交。事务2只能看到表1最初的数据,看不到新增的数据。
无论事务1对表1进行怎样的操作,提交后,事务2只能看到表1最初的数据。
演示第四级别:序列化读/串行化读(serializable)
设置事务的全局隔离级别:
set global transaction isolation level serializable;
查看事务的全局隔离级别:
select @@global.transaction_isolation;//mysql8.0+版本
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| SERIALIZABLE |
+--------------------------------+
现象:事务1不结束,事务2无法对表1操作。
事务1先操作表1,对表1新增1条数据(未提交);
然后事务2查询表1:select * from 表1; 回车后光标会停在那;
当事务1输入提交命令commit(代表事务1结束)时,事务2才出现查看结果。
索引就相当于一本书的目录,通过目录可以快速的找到对应的资源。
在数据库方面,查询一张表的时候有两种检索方式:
第一种方式:全表扫描
第二种方式:根据索引检索(效率很高)
索引为什么可以提高检索效率呢?
其实最根本的原理是缩小了扫描的范围。
索引虽然可以提高检索效率,但是不能随意的添加索引,因为索引也是数据库当中的对象,也需要数据库不断的维护。是有维护成本的。
比如:表中的数据经常被修改,这样就不适合添加索引,因为数据一旦修改,索引需要重新排序,进行维护。
添加索引是给某一个字段,或者说某些字段添加索引。
select ename,sal from emp where ename = 'SMITH';
当ename字段没有添加索引的时候,以上sql语句会进行全表扫描,扫描ename字段中所有的值。
当ename字段添加索引的时候,以上sql语句会根据索引扫描,快速定位。
创建索引对象:
create index 索引名称 on 表名(字段名);
删除索引对象:
drop index 索引名称 on 表名;
* 数据量庞大。(根据客户的需求,根据线上的环境)
* 该字段很少的DML操作。(因为字段进行修改操作,索引也需要维护)
* 该字段经常出现在where子句中。(经常根据哪个字段维护)
根据主键查询效率较高,尽量根据主键检索。
查看sql语句的执行计划:通过type ALL 知道是全表扫描(扫描了14次),不是根据索引扫描。
mysql> explain select ename,sal from emp where sal = 5000;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
给薪资sal字段添加索引:
create index emp_sal_index on emp(sal);
查看sql语句的执行计划:通过type ref 知道使用了索引,只扫描了1次。
mysql> explain select ename,sal from emp where sal = 5000;
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_sal_index | emp_sal_index | 9 | const | 1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
rows检索次数减少了
通过B Tree缩小扫描范围,底层索引进行了排序,分区,索引会携带数据在表中的"物理地址",最终通过索引检索到数据之后,获取到关联的物理地址,
通过物理索引检索到数据之后,获取到关联的物理地址,通过物理地址定位表中的数据,效率是最高的。
select ename from emp where ename = 'SMITH';
通过索引转换为:
select ename from emp where 物理地址 = 0x3;
单一索引:给单个字段添加索引
复合索引:给多个字段联合起来添加一个索引
主键索引:主键上会自动添加索引
唯一索引:有unique约束的字段会自动添加索引
一个表中索引只有1个
......
select ename from emp where ename like ' %A% ';
就算ename加了索引,也不会使用索引检索,因为不知道%是啥,查不到。
第一个不是%,就会使用索引。
模糊查询的时候,第一个通配符使用的是%,这个时候索引是是失效的。
站在不同的角度去看到数据(同一张表的数据,通过不同的角度去看待)
创建视图:create view myview as select empno,ename from emp;
删除视图:drop view myview;
注意:只有DQL语句(select语句)才能以试图对象的方式创建出来。
通过视图影响原表数据,不是直接操作的原表
可以对试图进行CRUD(增删改查)操作
//创建视图myview
create view myview as select empno,ename from emp;
mysql> select * from myview;
+-------+--------+
| empno | ename |
+-------+--------+
| 7369 | SMITH |
| 7499 | ALLEN |
| 7521 | WARD |
| 7566 | JONES |
| 7654 | MARTIN |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7788 | SCOTT |
| 7839 | KING |
| 7844 | TURNER |
| 7876 | ADAMS |
| 7900 | JAMES |
| 7902 | FORD |
| 7934 | MILLER |
+-------+--------+
//创建一个新表emp_bak
create table emp_bak as select * from emp;
//创建视图myview1
create view myview1 as select empno,ename,sal from emp_bak;
//通过视图修改原表数据
update myview1 set ename = 'hehe',sal = 1 where empno = 7369;
//通过视图删除原表数据
delete from myview1 where empno = 7369;
视图可以隐藏表的实现细节。
保密级别较高的系统,数据库只对外提供相关的视图,java程序员只对视图对象进行CRUD
CRUD增删改查
DBA:数据库管理员
在windows的DOS命令窗口中执行: (导出整个库)
mysqldump bjpowernode>D:\bjpowernode.sql -uroot -p999
mysqldump 数据库名>把数据库导到哪个地方 -uroot -p密码
在windows的dos命令窗口中执行:(导出数据库中指定的表)
mysqldump bjpowernode emp>D:\bjpowernode.sql -uroot -p999
mysqldump 数据库名 要导出的表名>把数据库导到哪个地方 -uroot -p密码
第一步:create database bjpowernode; //创建数据库 bjpowernode
第二步:use bjpowernode;
第三步:source D:\bjpowernode.sql //source+文件路径
设计表的依据。按照这三个范式设计的表不会出现数据冗余。
第一范式:任何一张表都应该有主键,并且每一个字段原子性不可再分。
------------------------------------------------------------------------
第二范式:建立在第一范式的基础上,所有非主键字段完全依赖主键,不能产生部份依赖。
上表中非主键字段对主键产生了部分依赖:
该主键是复合主键
学生姓名部分依赖了主键的一个字段学生编号,而没有依赖教师编号
而教师姓名部分依赖了主键的一个字段教师编号,而没有依赖学生编号
这就是第二范式部分依赖。
这样无法明确表示关系,产生了冗余(也是为什么不推荐使用复合主键)
解决办法:
多对多关系:三张表,学生表,老师表,学生老师关系表(两个外键)
这样就可以明确表示多对多关系(1个学生有多个老师,1个老师有多个学生)
典型的多对多设计口诀:多对多?三张表,关系表两个外键。
t_student学生表
sno(pk) sname
---------------------
1 张三
2 李四
3 王五
t_teacher 讲师表
tno(pk) tname
----------------------
1 王老师
2 张老师
3 李老师
t_student_teacher_relation 学生讲师关系表
id(pk) sno(fk) tno(fk) //该表tno引用讲师表tno(fk)
-------------------------------------------
1 1 3
2 1 1
3 2 2
4 2 3
5 3 1
6 3 3
以上是一种典型的“多对多”的设计
------------------------------------------------------------------------
第三范式:建立在第二范式的基础上,所有非主键字段直接依赖主键,不能产生传递依赖。
一对多?两张表,班级表,学生表(加外键)(多的表加外键)
一个班级对应多个学生
典型的一对多设计口诀:一对多?两张表,多的表加外键。
班级t_class
cno(pk) cname
--------------------------
1 班级1
2 班级2
学生t_student
sno(pk) sname classno(fk) //该表classno引用班级表cno(fk)
--------------------------------------------
101 张1 1
102 张2 2
103 张3 2
104 张4 1
105 张5 2
提醒:在实际的开发中,以满足客户需求为主,有的时候会拿冗余换执行速度。
表连接越多,sql语句执行速度越慢,单表执行速度快。
一对一:一张表拆成两张表,两张表有关系
一对一设计有两种方案:主键共享
t_user_login 用户登陆表
id(pk) username password
----------------------------------------
1 zs 123
2 ls 456
t_user_detail 用户详细信息表 //该表id引用用户登陆表id(fk)
id(pk+fk) realname tel ...
----------------------------------------------------
1 张三 11111111112234
2 李四 12112523432412
一对一设计有两种方案:外键唯一
t_user_login 用户登陆表
id(pk) username password
----------------------------------------
1 zs 123
2 ls 456
t_user_detail 用户详细信息表
id(pk) realname tel userid(fk+unique)//对外键添加唯一性,userid引用id(fk)
----------------------------------------------------
1 张三 111111114 1
2 李四 121432412 2
第一范式:任何一张表都应该有主键,并且每一个字段原子性不可再分。
第二范式:建立在第一范式的基础上,所有非主键字段完全依赖主键,不能产生部份依赖。
典型的多对多设计口诀:多对多?三张表,关系表两个外键。
第三范式:建立在第二范式的基础上,所有非主键字段直接依赖主键,不能产生传递依赖。
典型的一对多设计口诀:一对多?两张表,多的表加外键。
提醒:在实际的开发中,以满足客户需求为主,有的时候会拿冗余换执行速度。
表连接越多,sql语句执行速度越慢,单表执行速度快。
一对一设计有两种方案:
-主键共享:若两张表,给第二张表的主键再添加一个外键
-外键唯一:若两张表,给第二张表新加一个字段(该字段添加外键并添加唯一性约束)
第一步:取得每个部门的最高薪水
select deptno,max(sal) as maxsal from emp group by deptno;
+--------+---------+
| deptno | maxsal |
+--------+---------+
| 20 | 3000.00 |
| 30 | 2850.00 |
| 10 | 5000.00 |
+--------+---------+
第二步:将以结果作为临时表t,t表和emp表进行连接,
条件是:t.deptno = e.deptno and t.maxsal = e.sal
select
e.ename,t.*
from
emp e
join
(select deptno,max(sal) as maxsal from emp group by deptno) t
on
t.deptno = e.deptno and t.maxsal = e.sal;
+-------+--------+---------+
| ename | deptno | maxsal |
+-------+--------+---------+
| BLAKE | 30 | 2850.00 |
| SCOTT | 20 | 3000.00 |
| KING | 10 | 5000.00 |
| FORD | 20 | 3000.00 |
+-------+--------+---------+
注意:因为group by分组了,所以select后面只能跟分组函数和参与分组的字段(不能写ename)
第一步:求部门的平均薪水
select deptno,avg(sal) as avgsal from emp group by deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
第二步:每个部门中,哪些员工的薪水>该部门的平均薪水
将以上查询结果作为t表,t表与emp表连接
条件:部门编号相同,并且emp的sal大于t表的avgsal
select
e.deptno,e.ename,e.sal
from
emp e
join
(select deptno,avg(sal) as avgsal from emp group by deptno) t
on
e.deptno = t.deptno and e.sal>t.avgsal;
+--------+-------+---------+
| deptno | ename | sal |
+--------+-------+---------+
| 30 | ALLEN | 1600.00 |
| 20 | JONES | 2975.00 |
| 30 | BLAKE | 2850.00 |
| 20 | SCOTT | 3000.00 |
| 10 | KING | 5000.00 |
| 20 | FORD | 3000.00 |
+--------+-------+---------+
问题要求的是:平均的薪水等级:
第一步:查询所有员工的薪水等级
select e.deptno,e.ename,s.grade from emp e join salgrade s on e.sal between s.losal and hisal;
+--------+--------+-------+
| deptno | ename | grade |
+--------+--------+-------+
| 20 | SMITH | 1 |
| 30 | ALLEN | 3 |
| 30 | WARD | 2 |
| 20 | JONES | 4 |
| 30 | MARTIN | 2 |
| 30 | BLAKE | 4 |
| 10 | CLARK | 4 |
| 20 | SCOTT | 4 |
| 10 | KING | 5 |
| 30 | TURNER | 3 |
| 20 | ADAMS | 1 |
| 30 | JAMES | 1 |
| 20 | FORD | 4 |
| 10 | MILLER | 2 |
+--------+--------+-------+
第二步:将以上查询结果作为t表,按部门分组,求每个部门薪水等级的平均值
select
t.deptno,avg(t.grade)
from
(select e.deptno,e.ename,s.grade from emp e join salgrade s on e.sal between s.losal and hisal) t
group by
t.deptno;
+--------+--------------+
| deptno | avg(t.grade) |
+--------+--------------+
| 20 | 2.8000 |
| 30 | 2.5000 |
| 10 | 3.6667 |
+--------+--------------+
若为平均薪水的等级:
第一步:取得每个部门的平均薪水
select e.deptno,avg(sal) as avgsal from emp e group by e.deptno
+--------+-------------+
| deptno | avg(sal) |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
第二步:将以上查询结果作为t表,与salgrade表连接,查平均薪水的等级
select
t.deptno,s.grade
from
(select e.deptno,avg(sal) as avgsal from emp e group by e.deptno) t
join
salgrade s
on t.avgsal between s.losal and s.hisal
第一种方法:降序
select e.ename,e.sal from emp e order by e.sal desc limit 0,1;
//order by e.sal desc 按照薪资降序排列 取第一个
//limit 0,1 从0开始取1个
+-------+---------+
| ename | sal |
+-------+---------+
| KING | 5000.00 |
+-------+---------+
第二种方法:使用表的自连接
a表
+---------+
| sal |
+---------+
| 800.00 |
| 1600.00 |
| 1250.00 |
| 2975.00 |
| 1250.00 |
| 2850.00 |
| 2450.00 |
| 3000.00 |
| 5000.00 |
| 1500.00 |
| 1100.00 |
| 950.00 |
| 3000.00 |
| 1300.00 |
+---------+
b表
+---------+
| sal |
+---------+
| 800.00 |
| 1600.00 |
| 1250.00 |
| 2975.00 |
| 1250.00 |
| 2850.00 |
| 2450.00 |
| 3000.00 |
| 5000.00 |
| 1500.00 |
| 1100.00 |
| 950.00 |
| 3000.00 |
| 1300.00 |
+---------+
第一步:a表中每一个数据(除最大值5000)都能在b表中找到一个大于它的
select a.sal from emp a join emp b on a.sal < b.sal;
第二步:对第一步的结果去重(只有5000不在该表中)
select distinct a.sal from emp a join emp b on a.sal < b.sal;
+---------+
| sal |
+---------+
| 1300.00 |
| 950.00 |
| 1100.00 |
| 1500.00 |
| 1250.00 |
| 800.00 |
| 2450.00 |
| 2850.00 |
| 1600.00 |
| 2975.00 |
| 3000.00 |
+---------+
第三步:not in 找到不在表中的5000
select
sal
from
emp
where
sal
not in
(select distinct a.sal from emp a join emp b on a.sal < b.sal);
第三种方法:直接max
select max(sal) from emp e;
+----------+
| max(sal) |
+----------+
| 5000.00 |
+----------+
第一种方法:
第一步:求每个部门的平均薪水(部门编号+每个部门的平均薪水)
select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
第二步:在第一步的基础上,降序desc排列,limit 0,1 取第一个
select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno order by avgsal desc limit 0,1;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 10 | 2916.666667 |
+--------+-------------+
第二种方法:max
第一步:求每个部门的平均薪水
select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
第二步:
select max(t.avgsal) from (select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t;
+---------------+
| max(t.avgsal) |
+---------------+
| 2916.666667 |
+---------------+
第三步:
select
deptno,avg(sal) as avgsal
from
emp
group by
deptno
having
avgsal = (select max(t.avgsal) from (select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t);
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 10 | 2916.666667 |
+--------+-------------+
第一步:求每个部门的平均薪水(部门编号+平均薪水)
select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
第一步:将部门编号改为部门名称(以上结果为t表,与dept表连接)
select
d.dname,t.avgsal
from
dept d
join
(select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t
on
d.deptno = t.deptno;
+------------+-------------+
| dname | avgsal |
+------------+-------------+
| ACCOUNTING | 2916.666667 |
| RESEARCH | 2175.000000 |
| SALES | 1566.666667 |
+------------+-------------+
第三步:在第二步的基础上,降序排列,取第一个。
select
d.dname,t.avgsal
from
dept d
join
(select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t
on
d.deptno = t.deptno
order by
t.avgsal desc limit 0,1;
+------------+-------------+
| dname | avgsal |
+------------+-------------+
| ACCOUNTING | 2916.666667 |
+------------+-------------+
第一步:每个部门的平均薪水(部门编号+平均薪水)
select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
第二步:每个部门平均薪水的等级(部门编号+平均薪水)
将上表作为t表,与salgrade表连接
select
t.*,s.grade
from
(select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t
join
salgrade s
on
t.avgsal between s.losal and s.hisal;
+--------+-------------+-------+
| deptno | avgsal | grade |
+--------+-------------+-------+
| 20 | 2175.000000 | 4 |
| 30 | 1566.666667 | 3 |
| 10 | 2916.666667 | 4 |
+--------+-------------+-------+
第三步:注意:平均薪水等级最低的部门可能不止一个
通过min找出最低平均薪水等级,把第二步的结果作为p表
select
min(p.grade)
from
(select t.*,s.grade from (select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t join salgrade s on t.avgsal between s.losal and s.hisal) p;
+--------------+
| min(p.grade) |
+--------------+
| 3 |
+--------------+
第四步:从p表中找出和第三步结果相同的部门
select
p.*
from
(select t.*,s.grade from (select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t join salgrade s on t.avgsal between s.losal and s.hisal) p
where
p.grade = (select min(p.grade) from (select t.*,s.grade from (select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t join salgrade s on t.avgsal between s.losal and s.hisal) p);
+--------+-------------+-------+
| deptno | avgsal | grade |
+--------+-------------+-------+
| 30 | 1566.666667 | 3 |
+--------+-------------+-------+
第五步:将部门编号改为部门名称
将第四步的结果作为q表,与dept表连接
select
d.dname,q.*
from
(select p.* from (select t.*,s.grade from (select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t join salgrade s on t.avgsal between s.losal and s.hisal) p where p.grade = (select min(p.grade) from (select t.*,s.grade from (select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno) t join salgrade s on t.avgsal between s.losal and s.hisal) p)) q
join
dept d
on
d.deptno = q.deptno;
+-------+--------+-------------+-------+
| dname | deptno | avgsal | grade |
+-------+--------+-------------+-------+
| SALES | 30 | 1566.666667 | 3 |
+-------+--------+-------------+-------+
------------------------------------------------------------------
第二种方法:在于第三步的不同
平均薪水等级最低的部门可能不止一个
通过limit找出最低平均薪资,再找出对应的薪资等级,然后找出所有与之薪资等级相同的部门
第一步:每个部门的平均薪水(部门编号+平均薪水)
select e.deptno,avg(e.sal) as avgsal from emp e group by e.deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
将部门编号换为部门名称:
select
d.dname,avg(e.sal) as avgsal
from
emp e
join
dept d
on
e.deptno = d.deptno
group by
d.dname;
+------------+-------------+
| dname | avgsal |
+------------+-------------+
| RESEARCH | 2175.000000 |
| SALES | 1566.666667 |
| ACCOUNTING | 2916.666667 |
+------------+-------------+
第二步:求每个部门平均薪水的等级
将以上结果作为t表,与salgrade表连接
select
t.*,s.grade
from
(select d.dname,avg(e.sal) as avgsal from emp e join dept d on e.deptno = d.deptno group by d.dname) t
join
salgrade s
on
t.avgsal between s.losal and s.hisal;
+------------+-------------+-------+
| dname | avgsal | grade |
+------------+-------------+-------+
| RESEARCH | 2175.000000 | 4 |
| SALES | 1566.666667 | 3 |
| ACCOUNTING | 2916.666667 | 4 |
+------------+-------------+-------+
第三步:
1.通过limit找出最低平均薪资:
select avg(sal) as avgsal from emp group by deptno order by avgsal asc limit 0,1;
+-------------+
| avgsal |
+-------------+
| 1566.666667 |
+-------------+
2.再找出最低平均薪资对应的薪资等级
select
grade
from
salgrade
where
(select avg(sal) as avgsal from emp group by deptno order by avgsal asc limit 0,1) between losal and hisal;
+-------+
| grade |
+-------+
| 3 |
+-------+
3.基于第二步,然后找出所有与之薪资等级相同的部门
select
t.*,s.grade
from
(select d.dname,avg(e.sal) as avgsal from emp e join dept d on e.deptno = d.deptno group by d.dname) t
join
salgrade s
on
t.avgsal between s.losal and s.hisal
where
s.grade = (select grade from salgrade where (select avg(sal) as avgsal from emp group by deptno order by avgsal asc limit 0,1) between losal and hisal);
+-------+-------------+-------+
| dname | avgsal | grade |
+-------+-------------+-------+
| SALES | 1566.666667 | 3 |
+-------+-------------+-------+
mgr为领导字段
select distinct mgr from emp; //所有的领导代号 原领导表中有重复的代码
+------+
| mgr |
+------+
| 7902 |
| 7698 |
| 7839 |
| 7566 |
| NULL |
| 7788 |
| 7782 |
+------+
//看见有NULL要注意,如果有NULL,not in(...)所有的结果都是NULL,所以要排除NULL
select distinct mgr from emp where mgr is not NULL;
+------+
| mgr |
+------+
| 7902 |
| 7698 |
| 7839 |
| 7566 |
| 7788 |
| 7782 |
+------+
第一步:找出员工代码没有在mgr字段上出现的员工 的 最高薪水
select max(sal) from emp where empno not in(select distinct mgr from emp where mgr is not NULL);
+----------+
| max(sal) |
+----------+
| 1600.00 |
+----------+
第二步:找出薪水 比 普通员工的最高薪水 还要高 的领导
直接sal>1600就好了
就算有的领导薪水比员工低 也没关系 细品
员工代码没有在领导字段mgr出现,说明该员工是普通员工,不是领导
select empno,ename from emp where sal>(select max(sal) from emp where empno not in(select distinct mgr from emp where mgr is not NULL));
+-------+-------+
| empno | ename |
+-------+-------+
| 7566 | JONES |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7788 | SCOTT |
| 7839 | KING |
| 7902 | FORD |
+-------+-------+
select ename,sal from emp order by sal desc limit 0,5;
+-------+---------+
| ename | sal |
+-------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+
//此题没有考虑薪资一样的情况,可能第六名也是2850,不要深究了
//同上
select ename,sal from emp order by sal desc limit 5,5;
+--------+---------+
| ename | sal |
+--------+---------+
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
| WARD | 1250.00 |
+--------+---------+
//日期也可以升序降序排列
select ename,hiredate from emp order by hiredate desc limit 0,5;
+--------+------------+
| ename | hiredate |
+--------+------------+
| ADAMS | 1987-05-23 |
| SCOTT | 1987-04-19 |
| MILLER | 1982-01-23 |
| JAMES | 1981-12-03 |
| FORD | 1981-12-03 |
+--------+------------+
第一步:找出每个员工的薪水等级
select
e.ename,s.grade
from
emp e
join
salgrade s
on
e.sal between s.losal and s.hisal;
+--------+-------+
| ename | grade |
+--------+-------+
| SMITH | 1 |
| ALLEN | 3 |
| WARD | 2 |
| JONES | 4 |
| MARTIN | 2 |
| BLAKE | 4 |
| CLARK | 4 |
| SCOTT | 4 |
| KING | 5 |
| TURNER | 3 |
| ADAMS | 1 |
| JAMES | 1 |
| FORD | 4 |
| MILLER | 2 |
+--------+-------+
第二步:将以上结果继续按照grade分组统计count
select
s.grade,count(*) //count(*) 代表 记录每组有多少数据条数,也可以count(e.ename)/count(s.grade)
from
emp e
join
salgrade s
on
e.sal between s.losal and s.hisal
group by
s.grade;
+-------+----------+
| grade | count(*) |
+-------+----------+
| 1 | 3 |
| 3 | 2 |
| 2 | 3 |
| 4 | 5 |
| 5 | 1 |
+-------+----------+
有3个表S(学生表),C(课程表),SC(学生选课表)
S(SNO,SNAME)代表(学号,姓名)
C(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
SC(SNO,CNO,SCGRADE)代表(学号,课号,成绩)
问题:
1,找出没选过“黎明”老师的所有学生姓名。
2,列出2门以上(含2门)不及格学生姓名及平均成绩。
3,即学过1号课程又学过2号课所有学生的姓名。
请用标准SQL语言写出答案,方言也行(请说明是使用什么方言)
首先将三张表创建出来:
CREATE TABLE SC
(
SNO VARCHAR(200),
CNO VARCHAR(200),
SCGRADE VARCHAR(200)
);
CREATE TABLE S
(
SNO VARCHAR(200 ),
SNAME VARCHAR(200)
);
CREATE TABLE C
(
CNO VARCHAR(200),
CNAME VARCHAR(200),
CTEACHER VARCHAR(200)
);
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '1', '语文', '张');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '2', '政治', '王');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '3', '英语', '李');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '4', '数学', '赵');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '5', '物理', '黎明');
commit;
INSERT INTO S ( SNO, SNAME ) VALUES ( '1', '学生1');
INSERT INTO S ( SNO, SNAME ) VALUES ( '2', '学生2');
INSERT INTO S ( SNO, SNAME ) VALUES ( '3', '学生3');
INSERT INTO S ( SNO, SNAME ) VALUES ( '4', '学生4');
commit;
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '1', '40');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '2', '30');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '3', '20');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '4', '80');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '5', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '1', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '2', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '3', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '4', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '5', '40');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '3', '1', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '3', '3', '80');
commit;
S(SNO,SNAME)代表(学号,姓名)
C(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
SC(SNO,CNO,SCGRADE)代表(学号,课号,成绩)
select * from s; //学生表
+------+---------+
| SNO | SNAME |
+------+---------+
| 1 | 学生1 |
| 2 | 学生2 |
| 3 | 学生3 |
| 4 | 学生4 |
+------+---------+
select * from c; //课程表
+------+--------+----------+
| CNO | CNAME | CTEACHER |
+------+--------+----------+
| 1 | 语文 | 张 |
| 2 | 政治 | 王 |
| 3 | 英语 | 李 |
| 4 | 数学 | 赵 |
| 5 | 物理 | 黎明 |
+------+--------+----------+
select * from sc; //学生选课表
+------+------+---------+
| SNO | CNO | SCGRADE |
+------+------+---------+
| 1 | 1 | 40 |
| 1 | 2 | 30 |
| 1 | 3 | 20 |
| 1 | 4 | 80 |
| 1 | 5 | 60 |
| 2 | 1 | 60 |
| 2 | 2 | 60 |
| 2 | 3 | 60 |
| 2 | 4 | 60 |
| 2 | 5 | 40 |
| 3 | 1 | 60 |
| 3 | 3 | 80 |
+------+------+---------+
问题1:找出没选过“黎明”老师的所有学生姓名
//第一步:找出黎明老师所教课的课编号
select cno from c where cteacher = '黎明';
+------+
| cno |
+------+
| 5 |
+------+
//第二步:找出哪些学生上过黎明老师的课
select
sno
from
sc
where
cno = ( select cno from c where cteacher = '黎明');
+------+
| sno |
+------+
| 1 |
| 2 |
+------+
//第三步:没上过黎明老师的课的学生
select
sname
from
s
where
sno not in(select sno from sc where cno = ( select cno from c where cteacher = '黎明'));
+---------+
| sname |
+---------+
| 学生3 |
| 学生4 |
+---------+
问题2:列出2门以上(含2门)不及格学生姓名及平均成绩。
//a表 平均成绩
select sno,avg(scgrade) as avgsal from sc group by sno;
+------+--------+
| sno | avgsal |
+------+--------+
| 1 | 46 |
| 2 | 56 |
| 3 | 70 |
+------+--------+
//b表 2门以上(含2门)不及格学生姓名
select
s1.sno,s1.sname
from
s s1
join
(select sno,count(sno) as lowcount from sc where scgrade < 60 group by sno) t
on
s1.sno = t.sno and t.lowcount >= 2;
+------+---------+
| sno | sname |
+------+---------+
| 1 | 学生1 |
+------+---------+
//a表b表连接
select
b.sname,a.avgsal
from
(select sno,avg(scgrade) as avgsal from sc group by sno) a
join
(select s1.sno,s1.sname from s s1 join (select sno,count(sno) as lowcount from sc where scgrade < 60 group by sno) t on s1.sno = t.sno and t.lowcount >= 2) b
on
a.sno = b.sno;
+---------+--------+
| sname | avgsal |
+---------+--------+
| 学生1 | 46 |
+---------+--------+
问题3:既学过1号课程又学过2号课所有学生的姓名
//上过1号课程的学生名单
select sno from sc where cno = 1;
+------+
| sno |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
//上过2号课程的学生名单
select sno from sc where cno = 2;
+------+
| sno |
+------+
| 1 |
| 2 |
+------+
2 rows in set (0.00 sec)
//既上过1号课程又上过2号课程的学生名单
select
sname
from
s
where
sno in(select sno from sc where cno = 1) and sno in(select sno from sc where cno = 2);
+---------+
| sname |
+---------+
| 学生1 |
| 学生2 |
+---------+
左外连接(用自连接没有king)
select a.ename as '员工',b.ename as '领导' from emp a left join emp b on a.mgr = b.empno;
+--------+--------+
| 员工 | 领导 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| KING | NULL |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
自连接
emp a 员工表
emp b 领导表
select
a.ename as '员工',a.hiredate,b.ename as '领导', b.hiredate,d.dname
from
emp a
join
emp b
on
a.mgr = b.empno //直接上级
join
dept d
on
a.deptno = d.deptno
where
a.hiredate < b.hiredate; //早于
+--------+------------+--------+------------+------------+
| 员工 | hiredate | 领导 | hiredate | dname |
+--------+------------+--------+------------+------------+
| SMITH | 1980-12-17 | FORD | 1981-12-03 | RESEARCH |
| ALLEN | 1981-02-20 | BLAKE | 1981-05-01 | SALES |
| WARD | 1981-02-22 | BLAKE | 1981-05-01 | SALES |
| JONES | 1981-04-02 | KING | 1981-11-17 | RESEARCH |
| BLAKE | 1981-05-01 | KING | 1981-11-17 | SALES |
| CLARK | 1981-06-09 | KING | 1981-11-17 | ACCOUNTING |
+--------+------------+--------+------------+------------+
外连接 emp表和dept表
同时列出那些没有员工的部门,有NULL得外连接,内连接显示不出没有员工的部门
select
e.*,d.dname
from
emp e
right join
dept d
on
e.deptno = d.deptno;
+-------+--------+-----------+------+------------+---------+---------+--------+------------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO | dname |
+-------+--------+-----------+------+------------+---------+---------+--------+------------+
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 | ACCOUNTING |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 | ACCOUNTING |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 | ACCOUNTING |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 | RESEARCH |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 | RESEARCH |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 | RESEARCH |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 | RESEARCH |
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 | RESEARCH |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 | SALES |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 | SALES |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 | SALES |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 | SALES |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 | SALES |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 | SALES |
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | OPERATIONS |
+-------+--------+-----------+------+------------+---------+---------+--------+------------+
select
d.dname,t.count
from
(select e.deptno,count(*) as count from emp e group by e.deptno) t
join
dept d
on
t.deptno = d.deptno and t.count>=5;
+----------+-------+
| dname | count |
+----------+-------+
| RESEARCH | 5 |
| SALES | 6 |
+----------+-------+
另一种方法:
select deptno from emp group by deptno having count(*)>=5;
+--------+
| deptno |
+--------+
| 20 |
| 30 |
+--------+
select sal from emp where ename = 'SMITH';
+--------+
| sal |
+--------+
| 800.00 |
+--------+
select * from emp where sal>(select sal from emp where ename = 'SMITH');
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
select
e.ename,e.job,d.dname,d.deptno
from
emp e
join
dept d
on
e.deptno = d.deptno
where
e.job = 'CLERK';
+--------+-------+------------+--------+
| ename | job | dname | deptno |
+--------+-------+------------+--------+
| SMITH | CLERK | RESEARCH | 20 |
| ADAMS | CLERK | RESEARCH | 20 |
| JAMES | CLERK | SALES | 30 |
| MILLER | CLERK | ACCOUNTING | 10 |
+--------+-------+------------+--------+
//每个部门的人数?
select deptno,count(*) as deptcount from emp group by deptno;
+--------+-----------+
| deptno | deptcount |
+--------+-----------+
| 20 | 5 |
| 30 | 6 |
| 10 | 3 |
+--------+-----------+
//两表连接 第一张表t1 第二张表t2
select
t1.*,t2.deptcount
from
(select e.ename,e.job,d.dname,d.deptno from emp e join dept d on e.deptno = d.deptno where e.job = 'CLERK') t1
join
(select deptno,count(*) as deptcount from emp group by deptno) t2
on
t1.deptno = t2.deptno;
+--------+-------+------------+--------+-----------+
| ename | job | dname | deptno | deptcount |
+--------+-------+------------+--------+-----------+
| SMITH | CLERK | RESEARCH | 20 | 5 |
| ADAMS | CLERK | RESEARCH | 20 | 5 |
| JAMES | CLERK | SALES | 30 | 6 |
| MILLER | CLERK | ACCOUNTING | 10 | 3 |
+--------+-------+------------+--------+-----------+
select job,min(sal),count(*) from emp group by job having min(sal)>1500;
+-----------+----------+----------+
| job | min(sal) | count(*) |
+-----------+----------+----------+
| MANAGER | 2450.00 | 3 |
| ANALYST | 3000.00 | 2 |
| PRESIDENT | 5000.00 | 1 |
+-----------+----------+----------+
//按照job分组,找出每组中最低薪金min(sal)>1500的,输出job和count(*)条数
//查出部门编号
select deptno from dept where dname = 'SALES';
select ename from emp where deptno = (select deptno from dept where dname = 'SALES);
+--------+
| ename |
+--------+
| ALLEN |
| WARD |
| MARTIN |
| BLAKE |
| TURNER |
| JAMES |
+--------+
select
e.ename as '员工',d.dname,l.ename as '领导',s.grade
from
emp e
join
dept d
on
e.deptno = d.deptno
left join //内连会失去king,所以左外连
emp l
on
e.mgr = l.empno
join
salgrade s
on
e.sal between s.losal and s.hisal
where
e.sal > (select avg(sal) as avgsal from emp);
+--------+------------+--------+-------+
| 员工 | dname | 领导 | grade |
+--------+------------+--------+-------+
| FORD | RESEARCH | JONES | 4 |
| SCOTT | RESEARCH | JONES | 4 |
| CLARK | ACCOUNTING | KING | 4 |
| BLAKE | SALES | KING | 4 |
| JONES | RESEARCH | KING | 4 |
| KING | ACCOUNTING | NULL | 5 |
+--------+------------+--------+-------+
select
d.dname,e.ename,e.job
from
emp e
join
dept d
on
e.deptno = d.deptno
where
job = (select job from emp where ename = 'SCOTT');
+----------+-------+---------+
| dname | ename | job |
+----------+-------+---------+
| RESEARCH | SCOTT | ANALYST |
| RESEARCH | FORD | ANALYST |
+----------+-------+---------+
//部门编号30中的员工薪金
select sal from emp where deptno = 30;
+---------+
| sal |
+---------+
| 1600.00 |
| 1250.00 |
| 1250.00 |
| 2850.00 |
| 1500.00 |
| 950.00 |
+---------+
第一种:select e.ename,e.sal from emp e join (select sal from emp where deptno = 30) t on e.sal = t.sal and e.deptno<>30;
第二种:
select
ename,sal
from
emp
where
sal in(select sal from emp where deptno = 30) and deptno <> 30;
结果是没有
//部门30员工的最高薪金
select max(sal) from emp where deptno = 30;
+----------+
| max(sal) |
+----------+
| 2850.00 |
+----------+
select
e.ename,e.sal,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno
where
e.sal>(select max(sal) from emp where deptno = 30);
+-------+---------+------------+
| ename | sal | dname |
+-------+---------+------------+
| JONES | 2975.00 | RESEARCH |
| SCOTT | 3000.00 | RESEARCH |
| KING | 5000.00 | ACCOUNTING |
| FORD | 3000.00 | RESEARCH |
+-------+---------+------------+
select deptno,count(*),avg(sal) from emp group by deptno;
+--------+----------+-------------+
| deptno | count(*) | avg(sal) |
+--------+----------+-------------+
| 20 | 5 | 2175.000000 |
| 30 | 6 | 1566.666667 |
| 10 | 3 | 2916.666667 |
+--------+----------+-------------+
注意:部门40没有人
平均服务期限:服务期限是系统当前时间-入职时间
在mysql中如何计算两个日期的年差,差了多少年?=前一个日期-后一个日期
使用函数TimeStampDiff(间隔类型,前一个日期,后一个日期)
timestampdiff(YEAR,hiredate,now())
间隔类型:
SECOND 秒
MINUTE 分钟
HOUR 小时
DAY 天
WEEK 星期
MONTH 月
QUARTER 季度
YEAR 年
select
d.deptno,count(e.ename),ifnull(avg(e.sal),0) as avgsal,ifnull(avg(timestampdiff(YEAR,hiredate,now())),0) as avgservicetime
from
emp e
right join
dept d
on
e.deptno = d.deptno
group by
d.deptno;
+--------+----------------+-------------+----------------+
| deptno | count(e.ename) | avgsal | avgservicetime |
+--------+----------------+-------------+----------------+
| 10 | 3 | 2916.666667 | 40.0000 |
| 20 | 5 | 2175.000000 | 37.8000 |
| 30 | 6 | 1566.666667 | 40.0000 |
| 40 | 0 | 0.000000 | 0.0000 |
+--------+----------------+-------------+----------------+
//因为部门40没人,但要把部门40带上,就要使用右外连接,否则看不到部门40这条数据
ifnull(可能为null的字段,若为null设置为什么)
注意:count(*) 代表的是有几条数据(可能=要统计的个数,也可能不等于)
比如此题要统计每个部门的员工数目,40部门没有员工
使用count(*) 显示为1,有一条数据但不代表有一个员工
所以这里不能使用count(*),要使用count(e.ename)
select e.ename,d.dname,e.sal from emp e join dept d on e.deptno = d.deptno;
+--------+------------+---------+
| ename | dname | sal |
+--------+------------+---------+
| SMITH | RESEARCH | 800.00 |
| ALLEN | SALES | 1600.00 |
| WARD | SALES | 1250.00 |
| JONES | RESEARCH | 2975.00 |
| MARTIN | SALES | 1250.00 |
| BLAKE | SALES | 2850.00 |
| CLARK | ACCOUNTING | 2450.00 |
| SCOTT | RESEARCH | 3000.00 |
| KING | ACCOUNTING | 5000.00 |
| TURNER | SALES | 1500.00 |
| ADAMS | RESEARCH | 1100.00 |
| JAMES | SALES | 950.00 |
| FORD | RESEARCH | 3000.00 |
| MILLER | ACCOUNTING | 1300.00 |
+--------+------------+---------+
select
d.deptno,d.dname,d.loc,count(e.ename)
from
emp e
right join
dept d
on
e.deptno = d.deptno
group by
d.deptno,d.dname,d.loc; //按照这三个字段分组,select后面就可以写这三个字段
+--------+------------+----------+----------------+
| deptno | dname | loc | count(e.ename) |
+--------+------------+----------+----------------+
| 10 | ACCOUNTING | NEW YORK | 3 |
| 20 | RESEARCH | DALLAS | 5 |
| 30 | SALES | CHICAGO | 6 |
| 40 | OPERATIONS | BOSTON | 0 |
+--------+------------+----------+----------------+
select
e.ename,t.*
from
emp e
join
(select job,min(sal) as minsal from emp group by job) t
on
e.job = t.job and e.sal = t.minsal;
+--------+-----------+---------+
| ename | job | minsal |
+--------+-----------+---------+
| SMITH | CLERK | 800.00 |
| WARD | SALESMAN | 1250.00 |
| MARTIN | SALESMAN | 1250.00 |
| CLARK | MANAGER | 2450.00 |
| SCOTT | ANALYST | 3000.00 |
| KING | PRESIDENT | 5000.00 |
| FORD | ANALYST | 3000.00 |
+--------+-----------+---------+
select deptno,min(sal) from emp where job = 'MANAGER' group by deptno;
+--------+----------+
| deptno | min(sal) |
+--------+----------+
| 20 | 2975.00 |
| 30 | 2850.00 |
| 10 | 2450.00 |
+--------+----------+
select
ename,(sal+ifnull(comm,0))*12 as yearsal
from
emp
order by
yearsal asc;
+--------+----------+
| ename | yearsal |
+--------+----------+
| SMITH | 9600.00 |
| JAMES | 11400.00 |
| ADAMS | 13200.00 |
| MILLER | 15600.00 |
| TURNER | 18000.00 |
| WARD | 21000.00 |
| ALLEN | 22800.00 |
| CLARK | 29400.00 |
| MARTIN | 31800.00 |
| BLAKE | 34200.00 |
| JONES | 35700.00 |
| SCOTT | 36000.00 |
| FORD | 36000.00 |
| KING | 60000.00 |
+--------+----------+
自连接:emp a员工表 emp b领导表
将a表看作是员工表,b表看作是领导表
输出员工名和领导名即:a.ename,b.ename
a表和b表的关系就是员工领导关系,所以on后面写:a.mgr = b.empno a表中的员工是b表中的领导
条件是领导的薪水>3000,即b.sal>3000
select
a.ename as '员工',b.ename as '领导'
from
emp a
join
emp b
on
a.mgr = b.empno
where
b.sal>3000;
+-------+-------+
| 员工 | 领导 |
+-------+-------+
| JONES | KING |
| BLAKE | KING |
| CLARK | KING |
+-------+-------+
select
d.deptno,d.dname,d.loc,count(e.ename),ifnull(sum(e.sal),0) as sumsal
from
emp e
right join //右外连接,否则部门40的数据被舍去
dept d
on
e.deptno=d.deptno
where
d.dname like '%S%' //部门名称中带'S'字符
group by
d.deptno,d.dname,d.loc;
//按照d.deptno,d.dname,d.loc分组,分组之后查出来d.deptno,d.dname,d.loc,再对每一组计数和求和
+--------+------------+---------+----------------+----------+
| deptno | dname | loc | count(e.ename) | sumsal |
+--------+------------+---------+----------------+----------+
| 20 | RESEARCH | DALLAS | 5 | 10875.00 |
| 30 | SALES | CHICAGO | 6 | 9400.00 |
| 40 | OPERATIONS | BOSTON | 0 | 0.00 |
+--------+------------+---------+----------------+----------+
update emp set sal = sal*1.1 where timestampdiff(YEAR,hiredate,now())>30;
select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 880.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1760.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1375.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 3272.50 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1375.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 3135.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2695.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3300.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5500.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1650.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1210.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 1045.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3300.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1430.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
注:该文章部分转载自动力节点