什么是数据库?什么是数据库管理系统?什么是SQL?它们之间的关系是什么?
表:table,是数据库的基本组成单元,所有的数据都以表格的形式组织,目的是可读性强。
一个表包括行和列
行(row):被称为数据/记录(data)
列(column):被称为字段
每一个字段应该包含:字段名、数据类型、相关的约数
数据类型:字符串、数字、日期等
约束:例如,唯一性约束,使字段中的数据不能重复。类似这样。
使用创建好的数据库,执行如下命令:
mysql> source D:\MYSQL\bjpowernode.sql
xxx.sql这种文件被称为sql脚本文件,sql脚本文件中编写了大量的sql语句。我们执行sql脚本文件的时候,该文件中所有的sql语句会全部执行。
mysql> show tables;
±----------------+
| Tables_in_db_ld |
±----------------+
| dept |
| emp |
| salgrade |
±----------------+
查看各个表的具体结构
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 | |
±-------±------------±-----±----±--------±------+
desc 是describe的缩写。
退出mysql:exit
查看mysql中有哪些数据库:show databases;
选择使用某个数据库:use test;
创建数据库:create database db_ld;
查看数据库下有哪些表:show tables;
查看mysql数据库的版本号:select version();
查看当前使用的是哪个数据库:select database();
查看创建表时的语句:show create table emp;
mysql不见";"不执行,如果中途想停止:\c,终止命令的输入
查询一个字段:select 字段名 from 表名;
select和from都是关键字。
字段名和表名都是标识符。
查询前,可以先用 “desc 表名;”来查看表的字段名
查询多个字段:select dname,deptno from dept;
查询所有字段:
select a,b,c... from dept;
select * from dept;
给查询的列起别名
select deptno,dname as deptname from dept;
±-------±-----------+
| deptno | deptname |
±-------±-----------+
| 10 | ACCOUNTING |
| 20 | RESEARCH |
| 30 | SALES |
| 40 | OPERATIONS |
±-------±-----------+
注意:只是将显示的查询结果列名显示为deptname,原表列名还是dname。select语句永远不会进行修改操作。
select deptno,dname deptname from dept;
select deptno,dname 'dept name' from dept;
±-------±-----------+
| deptno | dept name |
±-------±-----------+
| 10 | ACCOUNTING |
| 20 | RESEARCH |
| 30 | SALES |
| 40 | OPERATIONS |
±-------±-----------+
别名用单引号括起来,也可以用双引号。不加引号则报错。
注意:在所有数据库当中,字符串统一使用单引号括起来,单引号是标准。双引号在Oracle数据库中用不了,在MySQL中可以用。
列参与数学运算
select ename,sal from emp;
±-------±--------+
| ename | sal |
±-------±--------+
| SMITH | 800.00 |
| ALLEN | 1600.00 |
| WARD | 1250.00 |
| JONES | 2975.00 |
| MARTIN | 1250.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| TURNER | 1500.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| FORD | 3000.00 |
| MILLER | 1300.00 |
±-------±--------+
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 |
±-------±---------+
将符合条件的数据查询出来。格式:
select
字段1,字段2,字段3...
from
表名
where
条件;
= 等于
查询薪资等于800的员工姓名和编号:
select empno,ename from emp where sal = 800;
查询SMITH的编号和薪资
select empno,sal from emp where ename = 'SMITH;'
<> != 不等于
select empno,ename from emp where sal != 800;
select empno,ename from emp where sal <> 800;
< <= > >=
select empno,ename,sal from emp where sal < 2000;
between … and … 。两个值之间,等同于 >= and <=
查询薪资在2450和3000之间的员工信息,包括2450和3000
第一种方式 >= and <= :select empno,ename,sal from emp where sal >= 2450 and sal <=3000;
第二种方式 between … and … :select empno,ename,sal from emp where sal between 2450 and 3000;
±------±------±--------+
| empno | ename | sal |
±------±------±--------+
| 7566 | JONES | 2975.00 |
| 7698 | BLAKE | 2850.00 |
| 7782 | CLARK | 2450.00 |
| 7788 | SCOTT | 3000.00 |
| 7902 | FORD | 3000.00 |
±------±------±--------+
注意:使用 between and 的时候,必须遵循左小右大;between and 是闭区间,包括两端的值。
is null 为 null(is not null 不为空)
查询哪些员工的补助/津贴为null:
select empno,ename,sal,comm from emp where comm is null;
注意:不能写成 … where comm = null;
在数据库当中null不能使用等号进行衡量,需要使用 is null,因为数据库中的null代表什么也没有,它不是一个值,所以不能使用等号衡量。
查询哪些员工的补助/津贴不为null:
select empno,ename,sal,comm from emp where comm is not null;
and 并且
查询工作岗位是MANGER并且工资大于2500的员工信息:
select
empno,ename,job,sal
from
emp
where
job = 'MANAGER' and sal > 2500;
or或者
select empno,ename,job from emp where job='MANAGER' or job='SALESMAN';
±------±-------±---------+
| empno | ename | job |
±------±-------±---------+
| 7499 | ALLEN | SALESMAN |
| 7521 | WARD | SALESMAN |
| 7566 | JONES | MANAGER |
| 7654 | MARTIN | SALESMAN |
| 7698 | BLAKE | MANAGER |
| 7782 | CLARK | MANAGER |
| 7844 | TURNER | SALESMAN |
±------±-------±---------+
and和or同时出现,要考虑优先级到的问题
查询工资大于2500,并且部门编号为10或20部门的员工:select * from emp where sal > 2500 and (deptno = 10 or deptno = 20);
and 和or同时出现,and优先级高。如果想让or先执行,需要加括号。以后开发中,如果不确定优先级,就加括号。
in 包含,相当于多个 or(not in 不在这个范围中)
查询工作岗位是MANAGER和SALESMAN的员工:
select empno,ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
select empno,ename,job from emp where job in('MANAGER', 'SALESMAN');
注意:in不是一个区间,in后面跟的是具体的值。
查询薪资是800和5000的员工信息:
select ename,sal from emp where sal in(800,5000);
±------±--------+
| ename | sal |
±------±--------+
| SMITH | 800.00 |
| KING | 5000.00 |
±------±--------+
not 可以取非,主要用在is或in中
select ename,sal from emp where sal not in(800,5000,3000);
is null、is not null
in、not in
like like 为模糊查询,支持%或下划线匹配
select ename from emp where ename like '%o%';
select ename from emp where ename like '%T';
select ename from emp where ename like '_A%';
select ename from emp where ename like '__R%';
select name from t_student where name like '%\_%';
默认为升序。
select
ename,sal
from
emp
order by
sal;
指定为降序。
select
ename,sal
from
emp
order by
sal desc;
指定为升序。
select
ename,sal
from
emp
order by
sal asc;
多个字段排序
查询员工名字和薪资,要求按照薪资升序,如果薪资一样的话,再按照名字升序排列。
select ename,sal from emp order by sal asc, ename asc;
sal在前,起主导,只有sal相等的时候,才会考虑启用ename排序。
根据字段的位置排序
按照查询结果的第2列sal排序:
select ename,sal from emp order by 2;
了解一下,实际开发中不建议这样写,因为不健壮,列的顺序容易发生改变,列的顺序发生修改后,2就废了。
综合一点的案例
找出工资在1250到3000之间的员工信息,要求按照薪资降序排列。
select
ename,sal
from
emp
where
sal between 1250 and 3000
order by
sal desc;
注意关键字的顺序,不能改变。
select … from … where … order by …(排序总是在最后执行)
在实际的应用中,可能有这样的需求,需要先进行分组,然后对每一组的数据进行操作,这个时候我们需要使用分组查询。
将之间的关键字全部组合在一起:
select … from … where … group by … order by…
执行顺序:from、where、group by、select、order by
注意:分组函数不能直接使用在where后面
select ename,sal from emp where sal > min(sal);
错误写法,因为分组函数在使用的时候必须先分组之后才能使用。where执行的时候,还没有分组。所以where后面不能出现分组函数。
select sum(sal) from emp;
可以使用,因为select在 group by 之后执行,group by 默认整张表是一组。(要充分理解SQL的执行顺序)
找出每个工作岗位的工资和:
思路:按照工作岗位分组,然后对工资求和。
select job,sum(sal) from emp group by job;
±----------±---------+
| job | sum(sal) |
±----------±---------+
| ANALYST | 6000.00 |
| CLERK | 4150.00 |
| MANAGER | 8275.00 |
| PRESIDENT | 5000.00 |
| SALESMAN | 5600.00 |
±----------±---------+
以上语句的执行顺序:先从emp表中查询数据,根据job字段进行分组,然后对每一组的数据进行sum(sal)。
select ename,job,sum(sal) from emp group by job;
,不要这样写,没有意义。在mysql中可以执行,其他数据库就报错了。因为ename有14条记录,而job只有5条。
重要结论:在一条select语句中,如果有group by语句的话,select后面只能跟:参加分组的字段,以及分组函数。其他的一律不能跟。
找出每个部门的最高薪资
实现思路:按照部门编号分组,求每一组的最大值。
select deptno,max(sal) from emp group by deptno;
±-------±---------+
| deptno | max(sal) |
±-------±---------+
| 10 | 5000.00 |
| 20 | 3000.00 |
| 30 | 2850.00 |
±-------±---------+
两个字段联合分组
找出“每个部门,不同工作岗位”的最高薪资:
select deptno, job, max(sal) from emp group by deptno, job;
mysql> select ename,job,sal,deptno from emp order by deptno;
±-------±----------±--------±-------+
| ename | job | sal | deptno |
±-------±----------±--------±-------+
| MILLER | CLERK | 1300.00 | 10 |
| KING | PRESIDENT | 5000.00 | 10 |
| CLARK | MANAGER | 2450.00 | 10 |
| FORD | ANALYST | 3000.00 | 20 |
| ADAMS | CLERK | 1100.00 | 20 |
| SCOTT | ANALYST | 3000.00 | 20 |
| JONES | MANAGER | 2975.00 | 20 |
| SMITH | CLERK | 800.00 | 20 |
| BLAKE | MANAGER | 2850.00 | 30 |
| MARTIN | SALESMAN | 1250.00 | 30 |
| TURNER | SALESMAN | 1500.00 | 30 |
| WARD | SALESMAN | 1250.00 | 30 |
| JAMES | CLERK | 950.00 | 30 |
| ALLEN | SALESMAN | 1600.00 | 30 |
±-------±----------±--------±-------+
mysql> select deptno, job, max(sal) from emp group by deptno, job;
±-------±----------±---------+
| deptno | job | max(sal) |
±-------±----------±---------+
| 10 | CLERK | 1300.00 |
| 10 | MANAGER | 2450.00 |
| 10 | PRESIDENT | 5000.00 |
| 20 | ANALYST | 3000.00 |
| 20 | CLERK | 1100.00 |
| 20 | MANAGER | 2975.00 |
| 30 | CLERK | 950.00 |
| 30 | MANAGER | 2850.00 |
| 30 | SALESMAN | 1600.00 |
±-------±----------±---------+
找出每个部门最高薪资,要求显示最高薪资大于3000的。
第一种:找出每个部门最高薪资,然后显示最高薪资大于3000的。select deptno,max(sal) from emp group by deptno having max(sal) > 3000;
使用having可以对分完组之后的数据进一步过滤,注意having不能单独使用,having不能代替where,having必须和group by联合使用。
第二种:先将大于3000的都找出来,然后再分组。select deptno, max(sal) from emp where sal > 3000 group by deptno;
这种方式的效率比上面的高一些。
优化策略:where和having,优先选择where,where实在完成不了了,再选择having。
找出每个部门平均薪资,要求显示平均薪资高于2500的。
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2500;
这题不能用where,可以对比上题的第二种方式看看。
select
...
from
...
where
...
group by
...
having
...
order by
... 以上关键字只能按照这个顺序来,不能颠倒。
执行顺序:1. from 2. where 3. group by 4. having 5. select 6. order by
从某张表中查询数据,
先经过where条件筛选出有价值的数据,
对这些有价值的数据进行分组。
分组之后可以使用having继续筛选。
select查询出来,
最后排序输出。
综合案例:
找出每个岗位的平均薪资,要求显示平均薪资大于1500的,除MANAGER岗位之外,要求按照平均薪资降序排。
select job,avg(sal) as avgsal from emp where job <> 'MANAGER' group by job having avg(sal) >1500 order by avgsal desc;
查询结果去除重复记录:distinct。
原表数据不会被修改,只是查询结果去重。
select distinct job from emp;
注意:distinct只能出现在所有字段的最前方
select ename,distinct job from emp;
这是错误写法。
在几个字段之前加上distinct,表示几个字段联合起来去重:
select distinct job,deptno from emp;
统计一下工作岗位的数量:select count(distinct job) from emp;
特点:一个输入对应一个输出。
字符串函数
upper、lower函数改变大小写
select lower(ename) from emp;
变成小写
±-------------+
| lower(ename) |
±-------------+
| smith |
| allen |
| ward |
| jones |
| martin |
| blake |
| clark |
| scott |
| king |
| turner |
| adams |
| james |
| ford |
| miller |
±-------------+
substr、substring函数截取,索引从1开始,没有0
select substr(ename, 1, 1) as ename from emp;
找出员工名字第一个字母是A的员工信息:
第一种方式:模糊查询
select ename from emp where ename like 'A%';
第二种方式:substr函数
select ename from emp where substr(ename,1,1) = 'A';
concat函数:拼接字符串
length函数用于获取参数值的字节个数
select length(ename) enamelength from emp;
首字母大写:
select concat(upper(substr(name,1,1)), substr(name,2,length(name) - 1 )) as result from t_student;
让首字母大写,拼接上后面的字符串。
instr函数返回字符串第一次出现的索引,若找不到,则返回0
trim函数去前后空格
select * from emp where ename = ' KING';
结果为空
select * from emp where ename = trim(' KING');
有记录
lpad函数用指定字符实现左填充指定长度
rpad函数同上
replace函数替换
数学函数
select round(1236.567, 0) as result from emp;
±-------+
| result |
±-------+
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
| 1237 |
±-------+
select round(1236.567, -1) as result from emp;
1240,保留到十位
select round(1236.567, -2) as result from emp;
1200,保留到百位
select round(rand()*100,0) from emp;
select ename,(sal+comm)*12 as yearsal from emp;
±-------±---------+
| ename | yearsal |
±-------±---------+
| SMITH | NULL |
| ALLEN | 22800.00 |
| WARD | 21000.00 |
| JONES | NULL |
| MARTIN | 31800.00 |
| BLAKE | NULL |
| CLARK | NULL |
| SCOTT | NULL |
| KING | NULL |
| TURNER | 18000.00 |
| ADAMS | NULL |
| JAMES | NULL |
| FORD | NULL |
| MILLER | NULL |
±-------±---------+
注意:Null只要参与了运算,结果为Null。为了避免这个想象,需要使用ifnull函数。用法:ifnull(数据,被当做哪个值)
select ename, (sal+ifnull(comm,0)) * 12 as yearsal from emp;
comm如果为null,当做0处理。
日期函数
now函数
获取当前时间,获取的时间带有:时分秒信息!是datetime类型的。
curdate函数返回当前日期,不包含时间
curtime函数返回当前时间,不包含日期
str_to_date函数
将字符串转化成日期类型
语法格式:str_to_date(‘字符串日期’, ‘日期格式’)。
str_to_date函数可以把字符串varchar转换成日期date类型数据,通常使用在插入insert方面,因为插入的时候需要一个日期类型的数据,需要通过该函数将字符串转换成date。
date_format函数
1. 将日期类型转换成特定格式的字符串。
date_format(birth, '%Y/%d/%m')
mysql默认格式:%Y-%d-%m。
2. 将日期转化为字符串格式
这个函数通常使用在查询日期方面,设置展示的日期格式。
timestampdiff(interval,datetime1,datetime2)函数
比较的单位interval可以为以下数值
FRAC_SECOND。表示间隔是毫秒
其他函数
case … when … then … when … then … else … end
例:当员工的工作岗位是MANAGER的时候,工资上调10%,当工作岗位是SALESMAN的时候,工资上调50%,其他正常。(不是修改数据库,只是查询结果显示为工资上调)
select
ename,
job,
sal as oldsal,
(case job when 'MANAGER' then sal*1.1 when 'SALESMAN' then sal*1.5 else sal end) as newsal
from
emp;
±-------±----------±--------±--------+
| ename | job | oldsal | newsal |
±-------±----------±--------±--------+
| SMITH | CLERK | 800.00 | 800.00 |
| ALLEN | SALESMAN | 1600.00 | 2400.00 |
| WARD | SALESMAN | 1250.00 | 1875.00 |
| JONES | MANAGER | 2975.00 | 3272.50 |
| MARTIN | SALESMAN | 1250.00 | 1875.00 |
| BLAKE | MANAGER | 2850.00 | 3135.00 |
| CLARK | MANAGER | 2450.00 | 2695.00 |
| SCOTT | ANALYST | 3000.00 | 3000.00 |
| KING | PRESIDENT | 5000.00 | 5000.00 |
| TURNER | SALESMAN | 1500.00 | 2250.00 |
| ADAMS | CLERK | 1100.00 | 1100.00 |
| JAMES | CLERK | 950.00 | 950.00 |
| FORD | ANALYST | 3000.00 | 3000.00 |
| MILLER | CLERK | 1300.00 | 1300.00 |
±-------±----------±--------±--------+
version()
database()
user()
format()
也叫多行处理函数,多个输入对应一个输出。
select max(sal) from emp;
注意:分组函数在使用的时候必须先进行分组,然后才能使用。如果没有对数据进行分组,整张表默认为一组。
分组函数的注意事项:
select count(comm) from emp;
返回结果为4;select sum(comm) from emp;
相加的时候忽略null,不会出现单行处理函数中与null进行计算结果为null的情况。count(*)
和count(具体字段)
有什么区别?select count(*) from emp;
select count(comm) from emp;
select ename,sal from emp where sal > min(sal);
错误写法select sum(sal),min(sal),max(sal),avg(sal),count(*) from emp;
当两张表进行连接查询,没有任何条件限制的时候,最终查询结果条数,是两张表条数的乘积,这种现象被称为笛卡尔积现象。(笛卡尔积发现的,这是一个数学现象。)
两张表连接没有任何条件限制:select ename,dname from emp,dept;
ename的一条记录会去匹配dname的所有记录。
怎么避免笛卡尔积现象?
连接时加条件,满足这个条件的记录被筛选出来!
select ename, dname from emp,dept where emp.deptno = dept.deptno;
select emp.ename, dept.dname from emp,dept where emp.deptno = dept.deptno;
select e.ename, d.dname from emp e, dept d where e.deptno = d.deptno;
起别名,很重要,效率问题。
最终结果是正确的,但是匹配的过程中,匹配的次数并没有减少,只是进行了筛选。
通过笛卡尔积现象得出,表的连接次数越多效率越低,尽量避免表的连接次数。
等值连接
查询每个员工所在部门名称,显示员工名和部门名:
SQL92语法:
select
e.ename,d.dname
from
emp e, dept d
where
e.deptno = d.deptno;
SQL92缺点:结构不清晰,表的连接条件,和后期进一步筛选的条件,都放在where后面。
SQL99语法:
select
e.ename, d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
SQL99优点:表连接的条件是独立的,连接之后,如果还需要进一步筛选,再往后继续添加where。
select
...
from
a
inner join //inner可以省略,带着inner可读性更好
b
on
a和b的连接条件
where
筛选条件
因为a和b的连接条件是等量关系,所以被称为等值连接。
非等值连接
找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级:
mysql> select * from emp;
mysql> select * from salgrade;
select
e.ename, e.sal, s.grade
from
emp e
join
salgrade s
on
e.sal between s.losal and s.hisal;
条件不是一个等量关系,所以称为非等值连接。
自连接
查询员工的上级领导,要求显示员工名和对应的领导名:
先查看员工表:select empno,ename,mgr from emp;
±------±-------±-----+
| empno | ename | mgr | //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 |
±------±-------±-----+
自连接技巧:一张表看成两张表,a表的mgr就是b表的empno
select
a.ename as '员工名', b.ename as '领导名'
from
emp a
join
emp b //自己和自己连
on
a.mgr = b.empno; //员工的领导编号 = 领导的员工编号
select
e.ename,d.dname
from
emp e
right join //right和join中间省略了outer,带着outer可读性强
dept d
on
e.deptno = d.deptno;
±-------±-----------+
| 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 |
| NULL | OPERATIONS | //内连接没有这条数据
±-------±-----------+
right代表:将join关键字右边的这张表看成主表,主要是为了将这张表的数据全部查询出来,捎带着关联查询左边的表。在外连接当中,两张表连接,产生了主次关系。
带有right的是右外连接,带有left的是左外连接;写法可以互换。
结论:外连接的查询结果条数一定是 >= 内连接的查询结果条数。
案例:查询每个员工的上级领导,要求显示所有员工和名字和领导名。
select
a.ename as '员工名', b.ename as '领导名'
from
emp a
left join
emp b
on
a.mgr = b.empno;
语法:
select
...
from
a
join
b
on
a和b的连接条件
join
c
on
a和c的连接条件
join
d
on
a和d的连接条件
一条SQL中内连接和外连接可以混合,都可以出现。
案例:找出每个员工的部门名称以及工资等级,要求显示员工名、部门名、薪资、薪资等级。
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 |
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 |
±------±------±------+
select
e.ename,d.dname,e.sal,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;
案例:找出每个员工的部门名称以及工资等级,还有上级领导,要求显示员工名、领导名、部门名、薪资、薪资等级。
select
e.ename,d.dname,e.sal,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
left join
emp l
on
e.mgr = l.empno;
select语句中嵌套select语句,被嵌套的select语句称为子查询。
子查询可以出现的位置:
select
..(select).
from
..(select).
where
..(select).
where子句中的子查询
案例:找出比最低工资高的员工姓名和工资
实现思路:
第一步:查询最低工资是多少
select min(sal) from emp;
第二步:找出>800的
select ename,sal from emp where sal > 800;
第三步:合并
select ename,sal from emp where sal > (select mini(sal) from emp);
from子句中的子查询
注意:from后面的子查询,可以将子查询的查询结果当做一张临时表。
案例:找出每个岗位的平均工资的薪资等级。
第一步:找出每个岗位的平均工资(按照岗位分组求平均值)
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 |
±----------±------------+
第二步:把以上的查询结果就当做一张真实存在的表t
select * from salgrade;
s表
±------±------±------+
| GRADE | LOSAL | HISAL |
±------±------±------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
±------±------±------+
第三步:t表和s表进行表连接
select
t.*, s.grade
from
(select job,avg(sal) as avgsal from emp group by job) t //注意这里的avgsal一定要起别名
join
salgrade s
on
t.avgsal between s.losal and s.hisal;
select子句中的子查询
案例:查询工作岗位是MANAGER和SALESMAN的员工:
select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
select ename,job from emp where job in('MANAGER', 'SALESMAN');
使用union:
select ename, job from emp where job = 'MANAGER'
union
select ename, job from emp where job = 'SALESMAN';
union的效率更高。对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的翻。
但是union可以减少匹配的次数。在减少匹配次数的情况下,还可以完成两个结果集的拼接。
举个例子:
a连接b连接c,a、b、c各10条记录,
匹配次数是:1000
使用union:a连接b:10*
10 -->100条记录,a连接c:10*
10 -->100条记录。
匹配次数:100 + 100 = 200次。
union在使用的时候注意事项:
1.进行结果集合并的时候,要求两个结果集的列数相同。
2.结果集合并时列和列的数据类型要相同。
作用:将查询结果集的一部分取出来。通常使用在分页查询当中。分页的作用是为了提高用户的体验,不用一次查询全部。
使用:limit startIndex, length
startIndex是起始下标,从0开始;length是长度。
按照薪资降序,取出排名在前5名的员工:
select
ename, sal
from
emp
order by
sal desc
limit 5; //取前五
注意:mysql当中limit在order by之后执行!
取出工资排名在[3-5]名的员工:
select
ename, sal
from
emp
order by
sal desc
limit
2, 3; //2代表起始位置从下标2开始,3就是三条记录 [3-5]
分页
每页显示3条记录:
第一页:limit 0, 3 [0 1 2]
第二页:limit 3, 3 [3 4 5]
第三页:limit 6, 3 [6 7 8]
第四页:limit 9, 3 [9 10 11]
每页显示pageSize条记录
第pageNo页:limit (pageNo - 1) * pageSize, pageSize
create table 表名 (
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型
);
表名:建议以 t_或者tbl_开始,可读性强。
mysql中常见的数据类型:
varchar(最长255):可变长度的字符串。比较智能,会根据实际的数据长度动态分配空间。
char(最长255):定长字符串。不管实际的数据长度是多少,分配固定长度的空间去存储数据,使用不恰当会导致空间的浪费。
优点:不需要动态分配空间,速度快。
缺点:使用不当会导致空间的浪费。
int(最长11)
bigint
float
double
date:短日期
datetime:长日期
clob:字符大对象,最多可以存储4G的字符串。比如:存储一篇文章。超过255个字符的都要采用clob来存储。
blob:二进制大对象。专门用来存储图片、声音、视频等流媒体数据。往blog类型字段上插入数据的时候,需要使用IO流。
创建一个学生表:
create table t_student (
no int,
name varchar(32),
sex char(1) default 'm', //可以指定默认值
age int(3),
email varchar(255)
);
CREATE TABLE IF NOT EXISTS user_info_vip (
id int(11) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '自增ID',
uid int unique NOT NULL COMMENT '用户ID',
nick_name varchar(64) COMMENT '昵称',
achievement int(11) default 0 COMMENT '成就值',
level int(11) COMMENT '用户等级',
job varchar(32) COMMENT '职业方向',
register_time datetime default CURRENT_TIMESTAMP COMMENT '注册时间'
)CHARACTER SET utf8 COLLATE utf8_general_ci;
删除表:drop table if exists t_student;
create table emp2 as select * from emp;
原理:将一个查询结果当做一张表新建,相当于复制了这张表。表创建出来,同时表中的数据也存在了。
create table mytable as select empno,ename from emp where job = 'MANAGER';
只复制表中一些字段。
使用 alter 操作:添加、删除、修改一个字段。
不常用:
第一:在实际开发中,需求一旦确定之后,表被设计好,很少会再去进行表结构的修改。因为开发进行中的时候,修改表结构成本比较高。修改表的结构对应的java代码就需要进行大量的修改。这个责任应该由设计人员来承担。
第二:由于修改表结构的操作很少,所以我们不需要掌握,如果有一天真的要修改表的结构,可以使用工具。
第三:修改表结构的操作是不需要写到java程序中的。
语法格式:insert into 表名(字段1, 字段2, 字段3, …) values(值1, 值2, 值3, …);
注意:字段名和值要一一对应,数量要对应,数据类型要对应。
insert语句中的字段名可以省略,省略字段名等于都写上了,所以值也要都写上。
insert into t_student values(2, 'lisi', 'f', 20, '[email protected]');
插入日期类型
create table t_user(
id int,
name varchar(32),
birth date
);
insert into t_user(id,name,birth) values(1, 'zhangsan', str_to_date('01-10-1990','%d-%m-%Y'));
mysql的日期格式:%Y 年、%m 月、%d日、%h 时、%i 分、%s 秒。
注意:如果提供的日期字符串是这个格式:%Y-%m-%d,str_to_date函数可以省略。
insert into t_user(id,name,birth) values(1, 'zhangsan', '1990-10-01');
查询日期类型
mysql默认使用%Y-%m-%d的格式。
如果不想用这个格式,可以使用date_format函数:
select id,name,date_format(birth, '%m/%d/%Y') as birth from t_user;
date和datetime两个类型的区别
date是短日期:只包括年月日信息。
datetime是长日期:包括年月日时分秒信息。
drop table if exists t_user;
create table t_user(
id int,
name varchar(32),
birth date, //短日期
create_time datetime //长日期,这条记录的创建时间
);
mysql短日期默认格式:%Y-%m-%d
mysql长日期默认格式:%Y-%m-%d %h:%i:%s
insert into t_user(id,name,birth,create_time) values(1, 'lisi', '1990-10-01', '2020-03-18 15:49:50');
可以结合now()函数使用,now()函数返回当前时间并且带有时分秒:insert into t_user(id,name,birth,create_time) values(1, 'lisi', '1990-10-01', now());
一次插入多条记录
insert into t_user(id, name, birth, create_time) values
(1, 'zs', '1980-10-11', now()),
(2, 'lisi', '1981-10-11', now()),
(3, 'wangwu', '1982-10-11', now());
将查询结果插入到一张表中
insert into dept_bak select * from dept;
语法格式:update 表名 set 字段名1 = 值1, 字段名2 = 值2, 字段名3 = 值3 … where 条件;
注意:没有条件限制会导致所有数据全部更新。
语法格式:delete from 表名 where 条件;
注意:没有条件,整张表的数据会全部删除!
delete from dept_bak;
删除表中所有的数据
删除的原理:表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放。
优点:支持回滚,后悔了可以再恢复。
缺点:删除效率比较低。
快速删除表中的数据
使用:truncate table dept_bak;
(属于DDL操作)
truncate tb_name
:全部删除(表清空,包含自增计数器重置)
原理:物理删除,表被一次截断,效率较高。
优点:快速。
缺点:不支持回滚。
说明:如果一张表非常大,比如有上亿条记录,使用delete执行可能需要1个小时才能删除完,效率低。选择使用truncate删除表中的数据,只需要不到1秒钟的时间就删除完,效率较高。但是在使用truncate之间,必须仔细询问客户是否真的要删除,并警告删除之后不可恢复。
constraint:在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性!
约束的作用就是为了保证:表中的数据有效!
约束包括:
非空约束:not null
唯一性约束:primary key
主键约束:primary key
外键约束:foreign key
检查约束:check(mysql不支持,Oracle支持)
所以,mysql重点学习4个约束。
非空约束:not null
非空约束所约束的字段不能为null。
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) not null
);
mysql> insert into t_vip(id) values(1);
ERROR 1364 (HY000): Field ‘name’ doesn’t have a default value
唯一性约束:unique
唯一性约束unique约束的字段不能重复,但是可以为NULL。
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) unique,
email varchar(255)
);
insert into t_vip(id,name,email) values(1,'wangwu','[email protected]');
如果再插入一条wangwu,就会报错
mysql> insert into t_vip(id,name,email) values(1,‘wangwu’,‘[email protected]’);
ERROR 1062 (23000): Duplicate entry ‘wangwu’ for key ‘name’
新需求:name和email两个字段联合起来具有唯一性!
create table t_vip(
id int,
name varchar(255) unique,
email varchar(255) unique //约束添加在列的后面,被称为列级约束
);
insert into t_vip(id,name,email) values(1,'wangwu','[email protected]');
insert into t_vip(id,name,email) values(2,'wangwu','[email protected]');
这样达不到新的需求,插入数据会报错,因为wangwu和wangwu重复。这样创建表示,name和email各自唯一。
两个字段联合唯一:
create table t_vip(
id int,
name varchar(255),
email varchar(255),
unique(name,email) //这样约束被称为表级约束
);
两个字段同时一样,才插入失败。
unique和not null 可以联合
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) not null unique
);
查看表结构:desc t_vip;
mysql> desc t_vip;
±------±-------------±-----±----±--------±------+
| Field | Type | Null | Key | Default | Extra |
±------±-------------±-----±----±--------±------+
| id | int(11) | YES | | NULL | |
| name | varchar(255) | NO | PRI | NULL | |
±------±-------------±-----±----±--------±------+
可以看到name的类型为PRI(主键)。
在mysql中,如果一个字段同时被 not null 和 unique 约束的话,该字段自动变成主键字段。(但Oracle不一样)
主键约束(primary key,简称PK)
主键约束的相关术语:主键约束、主键字段、主键值。
主键值是每一行记录的唯一标识。
注意:任何一张表都应该有主键,没有主键,表无效!
主键的特征:not null + unique(不能为空,同时不能重复)
添加主键约束:
create table t_vip(
id int primary key,
name varchar(255)
);
create table t_vip(
id int,
name varchar(255),
primary key(id)
);
复合主键(几个字段联合做主键)
create table t_vip(
id int,
name varchar(255),
email varchar(255),
primary key(id, name) //表级约束是联合的意思
);
insert into t_vip(id,name,email) values(1,'zhangsan','[email protected]');
insert into t_vip(id,name,email) values(1,'lisi','[email protected]');
在实际开发中不建议使用:复合主键。
因为主键值存在的意义就是这行记录的身份证号,只要意义达到即可。
错误写法:这不是联合。一张表不能添加两个主键。
create table t_vip(
id int primary key,
name varchar(255) primary key,
email varchar(255)
);
主键值建议使用的类型:
int、bigint、char等类型,不建议使用varchar做主键。
主键值一般都是数字,一般都是定长的!
主键也可以这样分类:
自然主键:主键值是一个自然数,和业务没关系。
业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。
在实际开发中使用自然主键较多,因为主键只要做到不重复就行,不需要有意义。业务主键不好,因为主键一旦和业务挂钩,那么当业务发生变动的时候,可能会影响到主键值,所以业务主键不建议使用。
在mysql中,有一种机制,可以帮助我们自动维护一个主键值:
create table t_vip(
id int primary key auto_increment, //auto_increment表示自增,从1开始
name varchar(255)
);
insert into t_vip(name) values('lisi');
insert into t_vip(name) values('zhangsan');
外键约束(foreign key,简称FK)
设计数据库表,描述“班级和学生”信息:
班级一张表、学生一张表
t_class 班级表
classno(pk) classname
100 北京市大兴区亦庄镇第二中学高三1班
101 北京市大兴区亦庄镇第二中学高三2班
t_student 学生表
no(pk) name cno(fk引用t_class这张表的classno)
1 jack 100
2 luck 100
3 lilei 100
4 zhaf 101
5 wan 101
6 zhaoliu 101
当cno字段没有任何约束的时候,可能会导致数据无效。
所以为了保证cno字段中的值都是t_class表中的classno,需要给cno添加一个外键约束。
注意:添加了外键约束,表与表之间产生了父子关系。
t_class是父表、t_student是子表。
删除表的顺序:先删子,再删父。
创建表的顺序:先创建父,再创建子。
删除数据的顺序:先删子,再删父。
插入数据的顺序:先插入父,再插入子。
drop table if exists t_student; //先删子
drop table if exists t_class;
create table t_class( //先创建父
classno int primary key,
classname varchar(255),
);
create table t_student(
no int primary key auto_increment,
name varchar(255),
cno int,
foreign key(cno) references t_class(classno)
);
注意:子表中的外键引用的附表中的某个字段,被引用的这个字段不一定是主键。但至少具有unique唯一性。
外键可以为null。
存储引擎是MySQL中特有的一个术语,其他数据库中没有。
实际上存储引擎是一个表存储/组织数据的方式,不同的存储引擎,表存储数据的方式不同。
查看表:show create table t_vip;
----------------------------------------+
| t_vip | CREATE TABLEt_vip
(
id
int(11) DEFAULT NULL,
name
varchar(255) NOT NULL,
UNIQUE KEYname
(name
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
mysql默认的存储引擎是:InnoDB
mysql默认的字符编码方式是:utf8
可以在建表的时候给表指定存储引擎,在小括号")"的右边使用:ENGINE来指定存储引擎;CHARSET来指定这张表的字符编码方式。
create table t_product(
id int primary key,
name varchar(255)
) engine=InnoDB default charset=gbk;
show engines \G
MyISAM存储引擎
用这种引擎创建出来的表,具有以下特征:
使用三个文件表示每个表:
格式文件 – 存储表结构的定义(mytable.frm)
数据文件 – 存储表行的内容(mytable.MYD)
索引文件 – 存储表上索引(mytable.MYI)
可被转换为压缩、只读表来节省空间
对于一张表来说,只要是主键,或者加有unique约束的字段上会自动创建索引。(索引相当于是一本书的目录,缩小扫描范围,提高检索效率)
MyISAM存储引擎特点:可被转换为压缩、只读表来节省空间,这是这种存储引擎的优势!!!
InnoDB存储引擎
mysql默认的存储引擎,同时也是一个重量级的存储引擎。
InnoDB支持事务,支持数据库崩溃后自动恢复机制。
InnoDB存储引擎最重要的特点是:非常安全。
它管理的表主要具有以下特征:
MEMORY存储引擎
使用MEMORY存储引擎的表,其数据存储在内存中,且行的长度固定,这两个特点使得MEMORY存储引擎非常快。
MEMORY存储引擎管理的表具有下列特征:
在数据库目录内,每个表均以 .frm格式的文件表示。
表数据及索引被存储在内存中。(目的就是快,查询快!)
表级锁机制。
不能包含TEXT或BLOB字段。
MEMORY存储引擎以前被称为HEAP引擎。
优点:查询效率最高,不需要和硬盘交互。
缺点:不安全,关机后数据消失。因为数据和索引都是在内存当中。
一个事务其实就是一个完整的业务逻辑,是一个最小的工作单元。要么同时成功,要么同时失败,不可再分。
假设转账,从A账户向B账户转账10000
A账户的钱减去10000(update语句)
B账户的钱加上10000(update语句)
这就是一个完整的业务逻辑
这两个update语句要求必须同时成功或者同时失败,才能保证钱是正确的。
只有DML语句才会有事务这一说,其他语句和事务无关。
insert、delete、update。
一旦你的操作涉及到增、删、改,那么就一定要考虑安全问题。
因为做某件事情的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事情都能一条DML语句搞定,那么事务就没有存在的价值了。
所以,本质上,一个事务就是多条DML语句同时成功,或者同时失败!
事务:就是批量的DML语句同时成功,或者同时失败。
事务是怎么做到多条DML语句同时成功和同时失败的?
InnoDB存储引擎:提供一组用来记录事务性活动的日志文件。
事务开启:
insert
delete
update
…
事务结束
在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。
在事务的执行过程中,我们可以提交事务,也可以回滚事务。
提交事务:清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。提交事务标志着,事务的结束,并且是一种全部成功的结束。
回滚事务:将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件。回滚事务标志着,事物的结束,并且是一种全部失败的结束。
提交和回滚实现
提交事务:commit;
回滚事务:rollback;(回滚是回滚到上一次的提交点)
事务对应的英文单词是:transaction
mysql中默认事务是自动提交的,就是每执行一条DML语句,则提交一次。
开启事务,则会关闭自动提交机制
start transaction;
insert into dept_bak values(10, 'abc', 'tj');
rollback; //回滚,则上面的insert语句被撤销
ACID
原子性(A):说明事务是最小的工作单元,不可再分。
一致性(C):所有事务要求,在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证数据的一致性。
隔离性(I):A事务和B事务之间具有一定的隔离。相当于多线程并发访问的每个线程。
持久性(D):事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据保存到硬盘上!
重点研究事务的隔离性
形象的说,A教室和B教室中间有一道墙,这道墙可以很厚,也可以很薄。越厚,隔离级别越高。
事务与事务之间有4个隔离级别
验证事务隔离级别
查看隔离级别:select @@tx_isolation
set global transaction isolation level read uncommitted;
set global transaction isolation level read committed;
set global transaction isolation level repeatable read;
set global transaction isolation level serializable;
索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。
一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。
索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。
根据SQL语句扫描:select * from t_user where name = 'jack'
,扫描name查找。
如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,MySQL会进行全扫描,会将name字段上的每一个值都比对一遍。效率比较低。
MySQL在查询方面主要就是两种方式:
第一种方式:全表扫描。
第二种方式:根据索引检索。
在mysql数据库当中,索引也是需要排序的,并且这个索引的排序和TreeSet数据结构相同。TreeSet底层是一个自平衡的二叉树!在mysql当中索引是一个B-Tree数据结构。
遵循左小右大原则存放,采用中序遍历方式遍历取数据。
假设有一张用户表:t_user
说明
1.在任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象。
2.在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
3.在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在。在MyISAM存储引擎中,索引存储在一个 .MYI文件中;在InnoDB存储引擎中,索引存储在一个逻辑名称叫做tablespace当中;在MEMORY存储引擎当中,索引被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树:B-Tree)
什么情况下,我们会考虑给字段添加索引呢?
1.数据量庞大(到底有多么庞大算庞大,这个需要测试,因为每一个硬件环境不同)。
2.该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。
3.该字段很少的DML操作,因为DML之后,索引需要重新排序。
建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。建议通过主键查询,建议通过unique约束的字段进行查询,效率会比较高。
create index emp_ename_index on emp(ename);
drop index emp_ename_index on emp;
在mysql当中,查看一个SQL语句是否使用了索引进行检索:
explain select * from emp where ename = 'KING';
索引也有失效的情况,如下:
"%"开头的模糊查询
select * from emp where ename like '%T';
尽量避免模糊查询的时候以"%"开头,这是一种优化的策略。
可以用SQL语句查看:explain select * from emp where ename like '%T';
使用or
如果使用or,那么要求or两边的条件字段都要有索引,索引才生效。如果其中一个字段没有索引,那么另一个字段上的索引也会失效。所以不建议使用or,可以使用union来代替。
使用复合索引
使用复合索引的时候,要使用左侧的列查找,索引才生效。使用右侧,则失效。
复合索引:两个字段,或者更多的字段联合起来添加一个索引。
create index emp_job_sal_index on emp(job,sal);
使用左侧:explain select * from emp where job = 'MANAGER';
生效
使用右侧:explain select * from emp where sal= 800;
失效
索引列参与运算
在where当中索引列参加了运算,索引失效。
explain select * from emp where sal+1 = 800;
索引列使用了函数
在where当中索引列使用了函数。
explain select * from emp where lower(ename) = 'smith';
还有其他情况,先了解这些…
索引是各种数据库进行优化的重要手段,优化的时候优先考虑的因素就是索引。
索引在数据库当中分了很多类:
单一索引:一个字段上添加索引。
复合索引:两个字段或者更多的字段上添加索引。
主键索引:主键上添加索引。
唯一性索引:具有unique约束的字段上添加索引。
(注意:唯一性比较弱的字段上添加索引的用处不大。)
例:需要在examination_info表创建以下索引,规则如下:
在duration列创建普通索引 idx_duration、在exam_id列创建唯一性索引 uniq_idx_exam_id、在tag列创建全文索引 full_idx_tag。
create index idx_duration on examination_info(duration);
create unique index uniq_idx_exam_id on examination_info(exam_id);
create fulltext index full_idx_tag on examination_info(tag);
view:站在不同的角度去看待同一份数据。
创建视图对象:create view dept2_view as select * from dept2;
删除视图对象:drop view dept2_view;
注意:只有DQL语句才能以view的形式创建。
create view view_name as 这里的语句必须是DQL语句;
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点:通过对视图的操作,会影响到原表数据。)
create view
emp_dept_view
as
select
e.name, e.sal, d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
update emp_dept_view set sal = 1000 where dname = 'ACCOUNTING'; //修改视图,原表的数据也会修改
视图对象在实际开发中到底有什么用?
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。
每一次使用这个SQL语句的时候都需要重新编写,很长,很麻烦。
可以把这条复杂的SQL语句以视图对象的形式新建。
在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发,并且利于后期的维护,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象映射的SQL语句。
我们以后面向视图开发的时候,使用视图的时候可以像使用table一样,可以对视图进行增删改查等操作。
视图不是在内存当中,视图对象也是存储在硬盘上,不会消失。
再次提醒:
视图对应的语句只能是DQL语句。
但是视图对象创建完成之后,可以对视图进行增删改查等操作。
mysqldump 数据库>D:\数据库.sql -uroot -p密码
mysqldump bjpowernode emp>D:\bjpowernode .sql -uroot -p密码
source D:\bjpowernode.sql
数据库设计范式:数据库表的设计依据,教你怎么进行数据库表的设计。
第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。
第二范式:建立在第一范式的基础上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
第三范式:建立在第二范式的基础上,要求所有非主键字段直接依赖主键,不要产生传递依赖。
三范式是面试官经常问的,一定要熟记于心!!!
设计数据表的时候,按照以上的范式进行,可以避免表中数据的冗余,空间的浪费。
第一范式
最核心,最重要的范式,所有表的设计都需要满足。
1.有主键;2.每个字段原子性不可再分
不满足,没有主键,联系方式可以再分
第二范式
建立在第一范式的基础上。
1.所有非主键字段必须完全依赖主键 2.不要产生部分依赖
产生部分依赖,数据冗余了。
为了让以上的表满足第二范式,需要三张表来表示多对多的关系!
多对多的设计:
三张表,关系表两个外键!
第三范式
建立在第二范式的基础上。
所有非主键字段直接依赖主键,不要产生传递依赖。
以上表是一对多的关系,满足第二范式,因为主键不是复合主键,没有产生部分依赖。
但不满足第三范式,班级名称依赖班级编号,班级编号依赖学生编号,产生传递依赖。造成数据冗余。
一对多的设计:
两张表,多的表加外键!
总结数据库表的设计:
嘱咐:
数据库设计三范式是理论上的,实践和理论有的时候有偏差。
最终的目的都是为了满足客户的需求,有的时候会拿冗余换执行速度。
因为在SQL中,表和表之间连接次数越多,效率越低。(笛卡尔积)
有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,并且对于开发人员来说,SQL语句的编写难度也会降低。
MySQL-老杜练习题:https://blog.csdn.net/weixin_45581692/article/details/123226267