MySQL进阶
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL进阶相关的知识,希望对你有一定的参考价值。
mysql进阶
创建数据库,添加数据表
首先,需要创建一个数据库,使用utf8编码集。
下面是Oracle中自带的四张表,在MySQL中的建表语句
/*
功能:创建 scott 数据库中的 dept 表
*/
create table dept(
deptno int unsigned auto_increment primary key COMMENT ‘部门编号‘,
dname varchar(15) COMMENT ‘部门名称‘,
loc varchar(50) COMMENT ‘部门所在位置‘
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘部门表‘;
/*
功能:创建 scott 数据库中的 emp 表
*/
create table emp(
empno int unsigned auto_increment primary key COMMENT ‘雇员编号‘,
ename varchar(15) COMMENT ‘雇员姓名‘,
job varchar(10) COMMENT ‘雇员职位‘,
mgr int unsigned COMMENT ‘雇员对应的领导的编号‘,
hiredate date COMMENT ‘雇员的雇佣日期‘,
sal decimal(7,2) COMMENT ‘雇员的基本工资‘,
comm decimal(7,2) COMMENT ‘奖金‘,
deptno int unsigned COMMENT ‘所在部门‘,
foreign key(deptno) references dept(deptno)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘雇员表‘;
/*
功能:创建数据库 scott 中的 salgrade 表,工资等级表
*/
create table salgrade(
grade int unsigned COMMENT ‘工资等级‘,
losal int unsigned COMMENT ‘此等级的最低工资‘,
hisal int unsigned COMMENT ‘此等级的最高工资‘
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘工资等级表‘;
/*
功能:创建数据库 scott 的 bonus 表,工资表
*/
create table bonus(
ename varchar(10) COMMENT ‘雇员姓名‘,
job varchar(9) COMMENT ‘雇员职位‘,
sal decimal(7,2) COMMENT ‘雇员工资‘,
comm decimal(7,2) COMMENT ‘雇员资金‘
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘工资表‘;
/*
功能:插入数据库 scott 中表 dept 的初始化数据
*/
INSERT INTO dept VALUES (10,‘ACCOUNTING‘,‘NEW YORK‘);
INSERT INTO dept VALUES (20,‘RESEARCH‘,‘DALLAS‘);
INSERT INTO dept VALUES (30,‘SALES‘,‘CHICAGO‘);
INSERT INTO dept VALUES (40,‘OPERATIONS‘,‘BOSTON‘);
/*
功能:插入数据库 scott 中表 emp 的初始数据
*/
INSERT INTO emp VALUES (7369,‘SMITH‘,‘CLERK‘,7902,‘1980-12-17‘,800,NULL,20);
INSERT INTO emp VALUES (7499,‘ALLEN‘,‘SALESMAN‘,7698,‘1981-2-20‘,1600,300,30);
INSERT INTO emp VALUES (7521,‘WARD‘,‘SALESMAN‘,7698,‘1981-2-22‘,1250,500,30);
INSERT INTO emp VALUES (7566,‘JONES‘,‘MANAGER‘,7839,‘1981-4-2‘,2975,NULL,20);
INSERT INTO emp VALUES (7654,‘MARTIN‘,‘SALESMAN‘,7698,‘1981-9-28‘,1250,1400,30);
INSERT INTO emp VALUES (7698,‘BLAKE‘,‘MANAGER‘,7839,‘1981-5-1‘,2850,NULL,30);
INSERT INTO emp VALUES (7782,‘CLARK‘,‘MANAGER‘,7839,‘1981-6-9‘,2450,NULL,10);
INSERT INTO emp VALUES (7788,‘SCOTT‘,‘ANALYST‘,7566,‘87-7-13‘,3000,NULL,20);
INSERT INTO emp VALUES (7839,‘KING‘,‘PRESIDENT‘,NULL,‘1981-11-17‘,5000,NULL,10);
INSERT INTO emp VALUES (7844,‘TURNER‘,‘SALESMAN‘,7698,‘1981-9-8‘,1500,0,30);
INSERT INTO emp VALUES (7876,‘ADAMS‘,‘CLERK‘,7788,‘87-7-13‘,1100,NULL,20);
INSERT INTO emp VALUES (7900,‘JAMES‘,‘CLERK‘,7698,‘1981-12-3‘,950,NULL,30);
INSERT INTO emp VALUES (7902,‘FORD‘,‘ANALYST‘,7566,‘1981-12-3‘,3000,NULL,20);
INSERT INTO emp VALUES (7934,‘MILLER‘,‘CLERK‘,7782,‘1982-1-23‘,1300,NULL,10);
/*
功能:插入数据库 scott 中表 salgrade 的初始数据
*/
INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);
关系图
分组查询
分组查询时,使用order by关键字,会按照关键字后的字段进行分组。
- GROUP_CONCAT(field):查看分组后某个字段的所有内容
案例:按照部门分组,查询每个部门中的所有员工
select `deptno` , GROUP_CONCAT(ename)
FROM `emp`
GROUP BY `deptno`
结果集:
10 CLARK,KING,MILLER
20 SMITH,JONES,SCOTT,ADAMS,FORD
30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES
- 多字段分组
order by后可以加多个字段,结果会先按照第一个字段就行排序,再按照第二个字段进行排序。
select `deptno` , `ename`
FROM `emp`
GROUP BY `deptno`,`ename`
结果集:
10 CLARK
10 KING
10 MILLER
20 ADAMS
20 FORD
20 JONES
20 SCOTT
20 SMITH
30 ALLEN
30 BLAKE
30 JAMES
30 MARTIN
30 TURNER
30 WARD
-
分组后使用聚合函数
- 查询每个部门id和每个部门的工资和
SELECT `deptno`,SUM(`sal`) FROM `emp` GROUP BY `deptno`
- 查询每个部门id和每个部门的人数
SELECT `deptno`,COUNT(`empno`) FROM `emp` GROUP BY `deptno`
- 查询每个部门名称以及每个部门工资大于1500的人数
SELECT deptno
,COUNT(empno)
FROM emp
WHERE sal > 1500
GROUP BY deptno
- having字句
having用于分组后的查询,一般配合聚合函数使用。
where是分组钱的数据进行条件判断。
```sql
SELECT `deptno`,COUNT(empno)
FROM `emp`
WHERE sal > 2000
GROUP BY `deptno`
HAVING SUM(sal) > 6000
SQL书写、执行顺序
-
这里说明的不是编写顺序,而是书写顺序,也就是语法规则中的先后顺序。
?
SELECT->FROM->WHERE->GROUP BY->HAVING->ORDER BY->LIMIT
-
下面是SQL的执行顺序
?
FROM->WHERE->GROUP BY->HAVING->SELECT->ORDER BY->LIMIT
MySQL使用Limit分页
limit关键字可以查询出结果集【分组后】【排序后】(详细顺序请看上方SQL执行顺序),从指定下标(下标从0开始)开始,查询若干条的数据。
案例:查询薪资最高的三名员工的信息
SELECT *
FROM emp
ORDER BY sal
limit 0,3
通过limit关键字,可以实现分页的效果。公式如下:
-- 以下程序为伪代码,并非实际可以运行的代码。
int curPage = 1;
int pageSize = 3;
-- 当前页为第1页 从0开始 ,(1-1)*3 = 0
-- 当前页为第2页 从3开始 ,(2-1)*3 = 3
-- 当前页为第3页 从6开始 ,(3-1)*3 = 6
-- 当前页为第4页 从9开始 ,(4-1)*3 = 9
-- 当前页为第5页 从12开始 ,(5-1)*3 = 12
SELECT *
FROM emp
ORDER BY sal
limit (curPage - 1) * pageSize , pageSize;
主键约束
-
在字段中指定主键可以使表中的每一条记录都是唯一的
-
一个表中只能存在一个主键
-
主键字段值不能为空
-
主键字段会添加索引
创建主键的方法:
CREATE TABLE tableName(field1 dataType PRIMARY KEY,field2 dataType...);
CREATE TABLE tableName(field1 dataType, field2 dataType, PRIMARY KEY(fieldName));
CREATE TABLE tableName(field1 dataType, field2 dataType, PRIMARY KEY(field1,field2));
以上三种方法是创建表的时候直接指定主键,还可以先创建表,再添加主键。
1、创建表
2、添加主键
ALTER TABLE tableName ADD CONSTRAINT PRIMARY KEY(field);
唯一约束
- 唯一约束可以约束某个字段不能重复
- 区别于主键,唯一约束可以指定多个字段
创建方法:
CREATE TABLE tableName(field1 dataType,field2 dataType UNIQUE);
自动增长
在MySQL中,自动增长一般用于主键字段,由于主键不可重复的特性,所以一般给其添加自动增长。或者使用UUID、雪花算法等来创建字段。
CREATE TABLE tableName(
id int PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) UNIQUE
)
非空约束
NOT NULL关键字,创建表时加在字段后
CREATE TABLE tableName(
id int PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) UNIQUE,
int age NOT NULL
)
默认值约束
DEFAULT value,添加字段的默认值
CREATE TABLE tableName(
id int PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) UNIQUE,
age int NOT NULL,
gender VARCHAR(2) DEFAULT ‘男‘
)
参照完整性
参照完整性是指两个表之间的一种对应关系
通常情况下可以通过设置两个表之间的主键、外键关系,或者编写两个表的触发器来实现
有对应参照完整性的两张表格,在对他们进行数据插入、更新、删除的过程中,系统都会将被修改表格与量一张对应表格进行对照,从而阻止一些不正确的操作。
(这里指的就是外键,翻译成人话就是,创建外键的字段是要参照哪个表中的哪个字段,被参照的字段只能出现参照表中有的值)
注意事项:
- 数据库的主键和外键类型一定要一致
- 两个表必须得要是InnoDB类型
- 设置参照完整性后,外键当中的内值,必须得是主键当中的内容
- 创建表时,设置外键,设置外键的表为字表,其对应的只存在主键的表为主表
-- 创建主表
CREATE TABLE std(
id int PRIMARY KEY,
name VARCHAR(50),
age INT
);
-- 创建从表
CREATE TABLE score(
sid int,
score int,
CONSTRAINT sc_st_FK FOREIGN KEY(sid) REFRENCES std(id)
);
注意:
当添加好上述表中内容时,从表如果想添加sid,那么必须添加id内已经存在的值,否则会报错
参照完整性主要是约束添加、修改、删除,对于查询没有太大的影响。
多表查询
创建一对多关系
多对多关系就是我们上面说的外间关系的表,其创建方法如下:
CREATE TABLE person(
id int primary key not null,
name varchar(50),
age int,
gender char(1)
);
CREATE TABLE car(
cid int primary key,
cname varchar(50),
color varcahr(20),
pid int,
constraint fk_person foregin key(pid) reference person(id)
);
创建多对多关系
学生和老师的关系就是多对多关系,创建方法如下
- 创建教师表
create table teacher(
tid int primary key auto_increment,
name varchar(50),
age int,
gender char(1) default ‘男‘
);
- 创建学生表
create table student(
sid int primary key auto_increment,
name varchar(50) not null,
age int,
gender char(1) default ‘男‘
);
- 创建学生与老师关系表
create table tae_stu_rel(
tid int,
sid int
);
- 添加外键
alter table tea_std_rel add constraint fi_tid foregin key(tid) references teacher(tid);
alter table tea_std_rel add constraint fi_sid foregin key(sid) references teacher(tid);
多对多种,要想删除老师表和学生表中的数据,必须先删除中间表中的关系,否则无法删除
中间表中的sid,tid必须是teacher、student中存在的值
为什么要拆分表?
一对多、多对多中拆分为若干表是为了避免出现大量的冗余数据,各自的表维护自己的信息,增加中间表记录表之间的关系,能使得查询的效率更高。
合并结果集
MySQL中可以使用UNION、UNION ALL两个关键字把两个查询的结果合并到一起
UNION
合并并且去掉重复的记录
SELECT * FROM 表1 UNION SELECT * FROM 表2
UNION ALL
合并两次查询的记录
SELECT * FROM 表1 UNION ALL SELECT * FROM 表2
案例:
create table a(
name varchar(10),
score int
);
create table b(
name varchar(10),
score int
);
insert into a values(‘a‘,10),(‘b‘,20),(‘c‘,30);
insert into b values(‘a‘,10),(‘b‘,20),(‘c‘,40);
使用UNION查询
SELECT `name`,`score`
FROM `a`
UNION
SELECT `name`,`score`
FROM `b`
-- 结果集
a 10
b 20
c 30
c 40
使用UNION ALL查询
SELECT `name`,`score`
FROM `a`
UNION ALL
SELECT `name`,`score`
FROM `b`
-- 结果集
a 10
b 20
c 30
a 10
b 20
c 40
注意事项:
- 被合并两个表,列数、类型必须相同。
- UNION的效率高于UINON ALL
- 当确认记录不会重复,或者明确要求相同的被合并的情况下 用UNION,否则用UNION ALL
笛卡尔积
假设两个集合为A={a,b},集合B={0,1,2}
则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)}
当使用一个SELECT语句查询两张和两张以上的表的时候,就会出现笛卡尔积的情况
SELECT *
FROM a,b
a 10 a 10
b 20 a 10
c 30 a 10
a 10 b 20
b 20 b 20
c 30 b 20
a 10 c 40
b 20 c 40
c 30 c 40
如何避免出现笛卡尔积?
实际查询中,我们希望得到的结果并不是上述的内容,而是没有笛卡尔积的情况,如何消除笛卡尔积呢?
方法1:查询时,使得主键和外键保持一致
SELECT *
FROM emp e,dept d
WHERE e.deptno = d.deptno
原理:逐行使用WHERE判断主键和外键的值是否相等,只留下相等的内容
内连接查询
等值连接
所谓等值连接就是只显示值相等的内容,和上面消除笛卡尔积的方法本质是一样的,只是写法不一样而已。
SELECT *
FROM emp e INNER JOIN dept d
ON e.deptno = d.deptno
其中ON后加主键和外键关系的判断,ON后还可以继续加WHERE
SELECT *
FROM emp e inner join dept d
ON e.deptno = d.deptno
WHERE sal > 1000
建立学生表、分数表、科目表
-- 学生表
create table student(
`id` bigint primary key auto_increment,
name varchar(20),
age int,
address varchar(20),
gender char(1)
);
-- 分数表
create table score(
`sid` bigint,
`cid` bigint,
`score` double
);
-- 课程表
create table `class`(
`id` bigint primary key auto_increment,
`name` varchar(50)
);
ALTER TABLE score ADD CONSTRAINT std_fk FOREIGN KEY(sid) REFERENCES student(id);
ALTER TABLE score ADD CONSTRAINT cls_fk FOREIGN KEY(cid) REFERENCES `class`(id);
INSERT INTO student VALUES(1001,‘张三‘,20,‘上海‘,‘男‘);
INSERT INTO student VALUES(1002,‘李四‘,22,‘上海‘,‘男‘);
INSERT INTO student VALUES(1003,‘王五‘,20,‘上海‘,‘女‘);
INSERT INTO student VALUES(1004,‘赵六‘,18,‘广州‘,‘男‘);
INSERT INTO student VALUES(1005,‘周七‘,23,‘上海‘,‘男‘);
INSERT INTO `class` VALUES(1,‘java‘);
INSERT INTO `class` VALUES(2,‘html‘);
INSERT INTO `class` VALUES(3,‘语文‘);
INSERT INTO `class` VALUES(4,‘数学‘);
INSERT INTO `class` VALUES(5,‘英语‘);
INSERT INTO score VALUES(1001,1,90);
INSERT INTO score VALUES(1001,2,70);
INSERT INTO score VALUES(1002,1,80);
INSERT INTO score VALUES(1002,2,50);
INSERT INTO score VALUES(1003,1,40);
INSERT INTO score VALUES(1004,1,90);
INSERT INTO score VALUES(1005,1,80);
- 查询学生各个科目的成绩
SELECT st.`name` , sc.`score`,`cl`.`name`
FROM student st,score sc,class cl
WHERE st.id = sc.sid
AND sc.cid = cl.id;
-- 使用join 只使用join和inner join的作用相同
SELECT student.`name`,class.`name`,score.score
FROM student
JOIN score on score.sid = student.id
JOIN class ON score.cid = class.id;
非等值连接
所谓非等值查询就是ON后的条件不是等价关系,可以使大于、小于等等的关系。
CREATE TABLE `emp` (
`empno` int(11) NOT NULL,
`ename` varchar(255) DEFAULT NULL,
`job` varchar(255) DEFAULT NULL,
`mgr` varchar(255) DEFAULT NULL,
`hiredate` date DEFAULT NULL,
`salary` decimal(10,0) DEFAULT NULL,
`comm` double DEFAULT NULL,
`deptno` int(11) DEFAULT NULL,
PRIMARY KEY (`empno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `emp` VALUES (7369, ‘孙悟空‘, ‘职员‘, ‘7902‘, ‘2010-12-17‘, 800, NULL, 20);
INSERT INTO `emp` VALUES (7499, ‘孙尚香‘, ‘销售人员‘, ‘7698‘, ‘2011-2-20‘, 1600, 300, 30);
INSERT INTO `emp` VALUES (7521, ‘李白‘, ‘销售人员‘, ‘7698‘, ‘2011-2-22‘, 1250, 500, 30);
INSERT INTO `emp` VALUES (7566, ‘程咬金‘, ‘经理‘, ‘7839‘, ‘2011-4-2‘, 2975, NULL, 20);
INSERT INTO `emp` VALUES (7654, ‘妲己‘, ‘销售人员‘, ‘7698‘, ‘2011-9-28‘, 1250, 1400, 30);
INSERT INTO `emp` VALUES (7698, ‘兰陵王‘, ‘经理‘, ‘7839‘, ‘2011-5-1‘, 2854, NULL, 30);
INSERT INTO `emp` VALUES (7782, ‘虞姬‘, ‘经理‘, ‘7839‘, ‘2011-6-9‘, 2450, NULL, 10);
INSERT INTO `emp` VALUES (7788, ‘项羽‘, ‘检查员‘, ‘7566‘, ‘2017-4-19‘, 3000, NULL, 20);
INSERT INTO `emp` VALUES (7839, ‘张飞‘, ‘总裁‘, NULL, ‘2010-6-12‘, 5000, NULL, 10);
INSERT INTO `emp` VALUES (7844, ‘蔡文姬‘, ‘销售人员‘, ‘7698‘, ‘2011-9-8‘, 1500, 0, 30);
INSERT INTO `emp` VALUES (7876, ‘阿珂‘, ‘职员‘, ‘7788‘, ‘2017-5-23‘, 1100, NULL, 20);
INSERT INTO `emp` VALUES (7900, ‘刘备‘, ‘职员‘, ‘7698‘, ‘2011-12-3‘, 950, NULL, 30);
INSERT INTO `emp` VALUES (7902, ‘诸葛亮‘, ‘检查员‘, ‘7566‘, ‘2011-12-3‘, 3000, NULL, 20);
INSERT INTO `emp` VALUES (7934, ‘鲁班‘, ‘职员‘, ‘7782‘, ‘2012-1-23‘, 1300, NULL, 10);
CREATE TABLE `dept` (
`deptno` bigint(2) NOT NULL AUTO_INCREMENT COMMENT ‘表示部门编号,由两位数字所组成‘,
`dname` varchar(14) DEFAULT NULL COMMENT ‘部门名称,最多由14个字符所组成‘,
`local` varchar(13) DEFAULT NULL COMMENT ‘部门所在的位置‘,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;
INSERT INTO `dept` VALUES (10, ‘财务部‘, ‘北京‘);
INSERT INTO `dept` VALUES (20, ‘调研部‘, ‘上海‘);
INSERT INTO `dept` VALUES (30, ‘销售部‘, ‘王者峡谷‘);
INSERT INTO `dept` VALUES (40, ‘运营部‘, ‘腾讯大楼‘);
CREATE TABLE `salgrade` (
`grade` bigint(11) NOT NULL AUTO_INCREMENT COMMENT ‘工资等级‘,
`lowSalary` int(11) DEFAULT NULL COMMENT ‘此等级的最低工资‘,
`highSalary` int(11) DEFAULT NULL COMMENT ‘此等级的最高工资‘,
PRIMARY KEY (`grade`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `salgrade` VALUES (1, 700, 1200);
INSERT INTO `salgrade` VALUES (2, 1201, 1400);
INSERT INTO `salgrade` VALUES (3, 1401, 2000);
INSERT INTO `salgrade` VALUES (4, 2001, 3000);
INSERT INTO `salgrade` VALUES (5, 3001, 9999);
- 查询所有员工的姓名,工资,所在部门的名称以及工资等级
SELECT emp.ename,emp.salary,dept.dname,salgrade.grade
FROM emp
JOIN dept ON dept.deptno = emp.deptno
JOIN salgrade
ON emp.salary BETWEEN salgrade.lowSalary
AND salgrade.highSalary
自然(natural)连接
上面案例中消除笛卡尔积的方法,我们需要指定主表和从表的关系,自然连接可以自动判断两个表的关系,无需添加条件即可消除笛卡尔积。
SELECT *
FROM emp e,dept d
WHERE e.deptno = d.deptno;
SELECT *
FROM emp
JOIN dept
ON emp.deptno = dept.deptno;
要求:
- 两张连接表中列名称和类型完全一致的列作为条件,例如A表为id,B表为cid就不可以。
- 会去除相同的列
SELECT * FROM emp NATURAL JOIN deptno;
外连接查询
左连接查询
LEFT JOIN ON
两个表中满足相同条件的数据查出来,如果左表中有不同的数据,把左表中的数据也查出来。
右连接查询
RIGIT JOIN ON
右链接和左连接相反,左边只要相同的内容,右边表中所有数据全部查出。
子查询
一个SELECT语句中包含了另一个完整的SELECT语句,或者两个以上SELECT,那么就是子查询语句了。
子查询可以出现的位置以及区别
- where之后,把select查询出的结果作为另一个select的条件值
- from之后,把查询出的结果作为一个新表(需要将表指定一个名字 看下述案例)
案例:查询与项羽相同部门的员工信息
SELECT *
FROM emp
WHERE emp.deptno = (
SELECT deptno
FROM emp
WHERE emp.ename = ‘项羽‘
);
案例:查询项羽所在部门中工资大于2000的员工信息
SELECT *
FROM
(SELECT * FROM emp WHERE deptno =
(SELECT deptno FROM emp WHERE emp.ename = ‘项羽‘) ) temp
WHERE salary > 2000;
-- 注意表一定要添加别名,否则会报错
练习:
1、查询工资高于程咬金的员工
SELECT *
FROM emp
WHERE salary > (
SELECT salary FROM emp WHERE ename = ‘程咬金‘
)
2、查询工资高于30号部门的所有员工信息
SELECT *
FROM emp
WHERE salary > (
SELECT MAX(salary) FROM emp WHERE deptno = 30
)
3、查询工作和工资与妲己完全相同的员工信息
SELECT * FROM emp WHERE job = (SELECT job FROM emp WHERE ename = ‘妲己‘)
AND salary = (SELECT salary FROM emp WHERE ename = ‘妲己‘)
方法2,这里应用了in可以指定多个字段,执行的sql更少 ,效率更高
SELECT * FROM emp WHERE (job,salary)
IN (SELECT job,salary FROM emp WHERE ename = ‘妲己‘)
4、有两个以上直接下属的员工信息
SELECT * FROM emp
WHERE empno IN(
SELECT mgr FROM emp GROUP BY mgr HAVING COUNT(*) >= 2
)
5、查询员工编号为7788的员工名称,员工工资,部门名称,部门地址
SELECT emp.ename,emp.salary,dept.dname,dept.`local`
FROM emp NATURAL JOIN dept WHERE emp.empno = 7788
分析:此问题应该先将项羽所在的部门编号查出,然后再查询该部门的所有员工。
自连接
所谓自连接就是子查询的时候自己连接自己。
案例:查询员工编号为7369的员工编号、姓名、经理编号、经理姓名
观察如下SQL
SELECT * FROM emp e1, emp e2
WHERE e1.mgr = e2.empno
可以看出,一条记录中包含了员工和其领导的完整数据。
指定查询条件即可。
SELECT e1.empno,e1.ename,e2.empno mgrId, e2.ename mgrName FROM emp e1, emp e2
WHERE e1.mgr = e2.empno
AND e1.empno = 7369
常用函数
字符串函数
函数名 | 介绍 |
---|---|
concat(s1,s2,sn...) | 将传入的字符串连接成字符串,任何与null连接的字符串都是null |
insert(str,x,y,inst) | 将字符串str从x位置开始,y个字符长的子串替换为指定的字符 |
LOWER(Str)和UPPER(str) | 将字符串转成小写或大写 |
LEFT(str,x)和RIGHT(str,x) | 分别返回字符串最左边的x个字符和最右边的x个字符 如果第二个参数为null,那么不返回任何字符 |
LPAD(str,n,pad)和RPAD(str,n,pad) | 用字符串pad对str最左边或最右边进行填充,直接到长度为n个字符长度 |
LTRIM(str)和RTRIM(str) | 去掉字符串当中最左侧和最右侧的空格 |
TRIM(str) | 去掉字符串左右的空格 |
REPEAT(str,x) | 返回str重复x次的结果 |
REPLACE(str,a,b) | 用字符串b替换字符串str中所有出现的字符串a. |
SUBSTRING(str,x,y) | 返回字符串str中第x位置起y个字符长度的字符 |
还有数值、日期、流程以及其他的函数,都可以使用java代码代替,在这里就不一一提了。
事务
事务是一组补课分割的操作,假设该操作有ABCD四个组成,如果ABCD都执行成功,则表示事务成功,如果ABCD中有任意一个操作失败,则为事务失败。
每条SQL语句都是一个事务,事务只对DML语句有效,对DQL无效。
事务的四大特性
-
原子性(Atomicity)
事务的操作要么同时成功,要么全部失败回滚
-
一致性(Consistency)
一致性是指事物必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性的状态。
让事务保持一定上的合理,例如A转账100元给B,A的账户减去100,B的账户加上100
-
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
-
持久性(Durability)
持久性是指一个事务一旦被提交了,就不能再回滚了,已经把数据保存到数据库当中了。
事务的使用
- 开启事务 start transaction
- 提交事务 commit 所有语句全部执行完毕,没有发生异常,提交事务,更新到数据库当中。
- 回滚事务 rollback 当遇到一突发情况,撤销执行的sql语句
事务的并发问题
-
脏读(读未提交)
1.财务将董震的工资从1000修改成了8000(但未提交事务)
2.此时应董震读取自己的工资发现自己的工资变成了8000,高兴的上蹦下跳
3.接着财务发现操作有误,回滚了事务,此时董震的工资又变成了1000,此时董震记取的工资8000是一个 脏数据解决办法:Read committed!读提交,能解决脏读问题
-
不可重复读
程序员拿着工资卡(卡里当然是只有3.6万),当他买单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了。程序员就会很郁闷,明明卡里是有钱的…
一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读
解决办法:Repeatable read
-
重复读
程序员拿着工资卡(卡里还是有3.6万),当他买时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
-
幻读
目前公司员工工资为1000的有10人
1.事务1读取所有的员工工资为1000的员工。
2.这时事务2向employee表插入了一条员工纪录,工资也为1000
3.事务1再次读取所有工资为1000的员工,共读取了11条记录。解决办法:Serializable
但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
事务的隔离级别
- Read uncommitted 就是一个事务可以读取另一个未提交事务的数据
- Read committed 一个事务要等另一个事务提交后才能读取数据
- Repeatable read 就是在开始读取数据(事务开启)时,不再允许修改操作
- Serializable 在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读
隔离级别 | 幻读 | 脏读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | |||
读提交(read-committed) | 可避免 | ||
可重复读(repeatable-read) | 可避免 | 可避免 | |
串行化(serializable) | 可避免 | 可避免 | 可避免 |
权限管理
权限就是限制一个用户在MySQL中可以操作哪些数据库、哪些表、哪些字段等,root用户拥有所有权限;
有哪些权限?
- create
- drop
- alter
- delete
- index
- insert
- select
- update
- create view
- execute
创建用户
create user ‘userName‘@‘host‘ identified by ‘pwd‘;
。。。
分配权限
。。。
视图
什么是视图
- 视图是一个虚拟表,其内容由查询定义
- 同真实表一样,视图包含一系列带有名称的列和行的数据
- 列和行数据来自定义视图的查询所引用的表,并且在引用视图时动态生成
- 简单地说视图是由SELECT结果组成的表
视图的特性
- 视图是对若干张基本表的引用,一张虚表,查询语句执行的结果
- 不存储具体的数据(基本表数据发生了改变,视图也会随之改变)
- 可以和基本表一样,进行增删改查操作(增删改操作有一定的限制)
视图的作用
- 安全性
- 查询性能提高
- 提高了数据的独立性
创建视图
创建视图的语法如下:
create view view_name as (select fieldList from tableName where con = ‘x‘);
-- 如果要覆盖一个视图
create or replace view view_name as (select fieldList from tableName where con = ‘x‘);
创建视图时,可以指定视图的创建方式,可以指定为merge(替换式)、temptable(具化式)。默认使用的是替换式。
- 使用替换式时,修改视图的同时会影响到数据库中的数据。
- 使用具化式不能更新视图中内容。
create [algorithm={megre|temptable}] view viewName as select fieldList FROM...;
还可以指定with check option,视图中只能更新符合视图限制的记录
create [algorithm={megre|temptable}] view viewName as select fieldList from... [WITH [CASCADED|LOCAL|]CHECK OPTION];
CASCADED和LOCAL为可选参数,决定了检查的范围 默认为CASCADED
删除视图
drop view view_name;
存储过程
什么是存储过程
存储过程就是一组可编程的函数,是为了完成特定功能的SQL语句集,存储过程就是具有名字的一段代码,用来完成一个特定的功能,创建的存储过程把存在数据库的数据字典中
为什么要用存储过程
-
将重复性很高的一些操作,封装到一个存储过程中,简化了对这些SQL的调用
-
批量处理
-
统一接口,确保数据的安全
-
相对于PRACLE数据库来说,MySQL的存储过程相对功能较弱,使用较少
DELIMITER
MySQL中,SQL只有遇到‘;‘号才会执行,由于存储过程都是多条SQL组成,所以使用分号很不合理,我们可以使用delimiter关键字来修改执行标志为$$;delimiter $$
,一般执行完存储过程之后,再修改为分号即可。
创建存储过程
delimiter $$
create procedure funName()
begin
sql list
end$$
delimiter ;
查看存储过程
- 查看所有SHOW PROCEDURE STATUS;
- 查看某个数据库的存储过程SHOW PROCEDURE STATUS WHERE db = ‘My_test4‘;
- 查看指定存储过程源代码SHOW CREATE PROCEDURE 存储过程名
使用存储过程
call funName();
索引
索引用于快速找出在某个列中有一特定值的行,
不使用索引,MySQL必须从第一条记录开始读完整个表,直到找出相关的行,
表越大,查询数据所花费的时间就越多,
如果表中查询的列有一个索引,MySQL能够快速到达一个位置去搜索数据文件,
而不必查看所有数据,那么将会节省很大一部分时间
索引的优势和劣势
类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本。
通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占空间的。
虽然索引大大提高了查询速度,同时确会降低更新表的速度,如对表进行INSERT、UPDATE、DELETE。
索引的分类
单值索引
即一个索引只包含单个列,一个表可以有多个单列索引。
唯一索引
索引列的值必须唯一,但允许有空值。
复合索引
一个索引包含多个列。
INDEX MultiIdx(id,name,age)
全文索引
只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT类型字段上使用全文索引
索引操作
创建索引
CREATE INDEX 索引名称 ON table (column[, column]...);
create INDEX salary_index ON emp(salary)
删除索引
DROP INDEX 索引名称 ON 表名
查看索引
show index from 表名;
Table
表名
Non_unique
如果索引不能包括重复词,则为0。如果可以,则为1。
Key_name
索引的名称
Seq_in_index
索引中的列序列号,从1开始。
Column_name
列名称。
Collation
列以什么方式存储在索引中。在MySQL中,有值‘A‘(升序)或NULL(无分类)。
Cardinality
索引中唯一值的数目的估计值。
过运行ANALYZE TABLE或myisamchk -a可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机会就越大。
Sub_part
如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。
Packed
指示关键字如何被压缩。如果没有被压缩,则为NULL。
Null
如果列含有NULL,则含有YES。如果没有,则该列含有NO。
Index_type
用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
Comment
索引备注信息
自动创建索引
在表上定义了主键时, 会自动创建一个对应的唯一索引
在表上定义了一个外键时,会自动创建一个普通索引
索引的结构
索引结构
先会对数据进行排序
btree索引
B+树索引
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接。
hash索引
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
什么时候需要用到索引
主键自动建立唯一索引
频繁作为查询条件的字段应该创建索引
查询中与其他表关联的字段,外键关系建立索引
频繁更新的字段不适合建立索引,因为每次更新不单单是更新了记录还会更新索引
WHERE条件里用不到的字段不创建索引
查询中排序的字段,排序的字段若通过索引去访问将大大提高排序速度
查询中统计或者分组字段
什么时候不需要索引
表记录太少
经常增删改的表
如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果
以上是关于MySQL进阶的主要内容,如果未能解决你的问题,请参考以下文章
我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段
我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段
我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段
我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段