MySQL基础-DQL语句与多表查询 -- pd的MySQL笔记
文章目录
-
- [MySQL基础-DQL语句与多表查询 -- pd的MySQL笔记](#MySQL基础-DQL语句与多表查询 -- pd的MySQL笔记)
- 多表查询
- 内连接(sql99)
-
- [外连接查询(Outer Join)](#外连接查询(Outer Join))
- 聚合函数
- 数据分组
- 子查询
- 索引
- MySQL事务
- 用户管理
- 分页查询
多表查询
笛卡尔积(Cartesian Product)是指在进行多表查询时,如果没有指定连接条件(即 JOIN 条件或 WHERE 条件来限制表之间的关系),数据库会将一张表中的每一行与另一张表中的每一行进行组合,从而生成所有可能的配对结果。
如果表 A 有 1000 行,表 B 有 1000 行,笛卡尔积会产生 1,000,000 行结果,这会极大消耗内存和 CPU。通常我们并不需要所有组合,而是希望根据某些关联字段(如外键)来连接表。
sql
SELECT * FROM students, courses;
这样就会产生笛卡尔积,即每个学生都会被分配给每个课程。
等值连接
为了确定一个雇员的部门名,需要比较 EMPLOYEES 表中的 DEPARTMENT_ID 列与DEPARTMENTS 表中的 DEPARTMENT_ID 列的值。在 EMPLOYEES 和DEPARTMENTS 表之间的关系是一个相等 (equijoin) 关系,即,两 个 表 中DEPARTMENT_ID 列的值必须相等。
等值连接特点:
- 多表等值连接的结果为多表的交集部分;
- n表连接,至少需要n-1个连接条件;
- 多表不分主次,没有顺序要求;
- 一般为表起别名,提高阅读性和性能;
- 可以搭配排序、分组、筛选....等子句使用;
等值连接的两个列可以没有主外键关系,但必须满足等值连接条件。(没有主外键约束可能有出错的风险)
等值连接也被称为简单连接 (simple joins) 或内连接 (inner joins)。
sql
SELECT employees.employee_id, employees.last_name, employees.department_id, departments.department_id, departments.location_id FROM employees, departments where employees.dept_id = departments.dept_id;
限制不明确的列名
- 在多表中使用表前缀限制修饰列名。需要在 WHERE 子句中用表的名字限制列的名字以避免含糊不清。没有表前缀,DEPARTMENT_ID 列可能来自 DEPARTMENTS 表,也可能来自 EMPLOYEES 表,这种情况下需要添加表前缀来执行查询。
- 用表前缀改善性能。如果列名在两个表之间不相同,就不需要限定列。但是,使用表前缀可以改善性能,因为MySQL服务器可以根据表前缀找到对应的列。
- 用列别名区别有相同名称,但在不同表中的列。必须限定不明确的列名也适用于在其它子句中可能引起混淆的那些列,例如 SELECT子句或 ORDER BY 子句。
sql
SELECT e.employee_id, e.last_name, e.department_id, d.department_id, d.location_id FROM employees e, departments d where e.dept_id = d.dept_id;
增加搜索条件, 使用AND操作符增加搜索条件
显示每个雇员的 last name、departmentname 和 city
sql
select e.last_name, d.department_name, l.city
from employees e, departments d, locations l
where e.department_id = d.department_id
and d.location_id = l.location_id;
非等值连接
一个非等值连接是一种不同于等值操作的连接条件。 EMPLOYEES 表 和JOB_GRADES A 表之间的关系有一个非等值连接例子。在两个表之间的关系是EMPLOYEES 表中的 SALARY 列必须是 JOB_GRADES 表的 LOWEST_SALARY 和HIGHEST_SALARY 列之间的值。使用不同于等于 (=) 的操作符获得关系。

查询所有雇员的薪水级别
sql
select e.last_name, j.grade_level from employees e, job_grades j where e.salary between j.lowest_sal and j.highest_sal;
自连接
连接一个表到它自己。有时需要连接一个表到它自己。为了找到每个雇员的经理的名字,则需要连接EMPLOYEES 表到它自己,或执行一个自连接。
查询每个雇员的经理的名字以及雇员的名字,雇员名字列别名为W,经理列别名为M。
sql
select worker.last_name as W, manager.last_name as M from employees worker, employees manager where worker.manager_id = manager.employee_id;
交叉连接
使用交叉连接查询 employees 表与 departments 表。该连接和两个表之间的笛卡尔乘积是一样的。
sql
select * from employees cross join departments;
自然连接
连接只能发生在两个表中有相同名字和数据类型的列上。如果列有相同的名字,但数据类型不同,NATURAL JOIN 语法会引起错误。
显示每个部门的departmentname、id和city
sql
select department_id, department_name, location_id, city
from departments
natural join locations;
前提:departments和locations表中都有location_id列且有相同的数据类型。
内连接(sql99)
- SELECT 查询列表;
- FROM 表1 别名;
- INNER JOIN 连接表(INNER关键字可省略);
- ON 连接条件;
用ON子句查询员工号、员工姓名、部门号、部门名称:
sql
select e.employee_id, e.last_name, e.department_id, d.department_id, d.department_name
from employees e join departments d
on e.department_id = d.department_id;
进一步查询部门的城市, 且员工名字中包含e
sql
select e.employee_id, e.last_name, e.department_id, d.department_id, d.department_name, l.city
from employees e
join departments d
on e.department_id = d.department_id
join locations l
on d.location_id = l.location_id;
where e.last_name like '%e%';
外连接查询(Outer Join)
- 连接两个表,仅返回匹配的行的连接,称为内连接。
- 两个表之间的连接,返回内连接的结果,同时还返回不匹配行的左(或右)表的连接,称为左(或右)外连接
- 在两表之间的连接,返回内连接的结果,并返回左(或右)表中不匹配行的连接,称为全外连接
孤儿数据(Orphan Data):孤儿数据是指被连接的列的值为空的数据。
左外连接 查询员工信息和部门信息;会显示所有员工信息,即使没有部门信息
sql
select e.employee_id, e.last_name, e.department_id, d.department_name
from employees e
left outer join departments d
on e.department_id = d.department_id;
右外连接 :查询部门信息和员工信息;会显示所有部门信息,即使没有员工信息
sql
select e.employee_id, e.last_name, e.department_id, d.department_name
from employees e
right outer join departments d
on e.department_id = d.department_id;
全外连接使用union实现,可以将两个查询结果集合并,返回的行都是唯一的,如同对整个结果集合使用了DISTINCT。
union all则是将两个结果集合并,返回的行可能会重复。
全外连接
sql
-- 左连接
(select e.employee_id, e.last_name, e.department_id, d.department_name
from employees e
left outer join departments d
on e.department_id = d.department_id;)
union
-- 右连接
(select e.employee_id, e.last_name, e.department_id, d.department_name
from employees e
right outer join departments d
on e.department_id = d.department_id;)
聚合函数
聚合函数也称之为多行函数,组函数或分组函数。聚合函数不象单行函数,聚合函数对行的分组进行操作,对每组给出一个结果。如果在查询中没有指定分组,那么聚合函数则将查询到的结果集视为一组。
聚合函数类型
- AVG 平均值
- COUNT 计数
- MAX 最大值
- MIN 最小值
- SUM 求和
语法:
sql
SELECT 聚合函数(字段名) FROM 表名 [WHERE 条件] [GROUP BY 分组字段]
[HAVING 分组条件] [ORDER BY 排序字段] [LIMIT 限制行数];
使用聚合函数的原则
- DISTINCT 使得函数只考虑不重复的值;
- 所有聚合函数忽略空值。为了用一个值代替空值,用 IFNULL 或 COALESCE 函数。
聚合函数不能互相嵌套, 但是聚合函数可以嵌套在单行函数中,单行函数也可以嵌套在聚合函数中。
AVG和SUM函数
- AVG 返回平均值 arg参数类型为数字
- SUM 返回和 arg参数类型为数字
如果参数类型不正确,会返回0;
示例:计算员工表中工作编号含有REP的员工平均工资和总工资
sql
SELECT AVG(salary), SUM(salary)
FROM employees
WHERE job_id LIKE '%REP%';
MIN和MAX函数
- MIN 返回最小值 arg参数类型为数字、字符、日期
- MAX 返回最大值 arg参数类型为数字、字符、日期
示例: 查询员工表中入职时间最短、最长员工
sql
SELECT MIN(hire_date), MAX(hire_date)
FROM employees;
COUNT函数
COUNT函数有三种格式
- COUNT(*) 统计行数,包括重复行、NULL行
- COUNT(expr) 返回列中由expr指定的非空值的数
- COUNT(DISTINCT expr) 返回在列中由expr指定的唯一的非空值的行数
示例: 返回部门编号为80佣金不为空的员工个数
sql
SELECT COUNT(commission_pct)
FROM employees
WHERE department_id = 80;
显示员工表中的部门个数
sql
SELECT COUNT(DISTINCT department_id)
FROM employees;
在组函数中使用IFNULL函数
sql
select avg(ifnull(commission_pct,0))
from employees;
数据分组
在没有进行数据分组之前,所有聚合函数是将结果集作为一个大的信息组进行处理。但是,有时,则需要将表的信息划分为较小的组,可以用 GROUP BY 子句实现。
语法:
sql
SELECT column_name, group_function(column_name)
FROM table_name
WHERE condition
GROUP BY group_by_expression
ORDER BY column_name;
原则
- 使用 WHERE 子句,可以在划分行成组以前过滤行。
- 如果有WHERE子句,那么GROUP BY 子句必须在WHERE的子句后面。
- 在 GROUP BY 子句中必须包含列。
示例:计算每个部门的员工总数 以及每个部门的平均工资
sql
SELECT department_id, COUNT(employee_id), AVG(salary)
FROM employees
GROUP BY department_id;
多列分组
先对department_id分组 在对同一个department_id下的job_id分组
计算每个部门的每个职位的职位总数 以及职位的工资总额
sql
SELECT department_id, job_id, COUNT(*), SUM(salary)
FROM employees
GROUP BY department_id, job_id;
分组后的过滤(HAVING)
HAVING 子句是对查询出结果集分组后的结果进行过滤。
- 用部门号分组,在每个部门中找最大薪水。
- 返回那些有最高薪水大于 $10,000 的雇员的部门
sql
SELECT department_id, MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 10000;
HAVING 子句 与 WHERE 子句 的区别
| 特性 | WHERE 子句 | HAVING 子句 |
|---|---|---|
| 作用对象 | 作用于原始表中的行(在分组前过滤) | 作用于分组后的结果集(在分组后过滤) |
| 使用时机 | 在 GROUP BY 之前执行 |
在 GROUP BY 之后执行 |
| 能否使用聚合函数 | 不能使用聚合函数(如 COUNT(), SUM() 等) |
可以使用聚合函数 |
| 性能影响 | 通常更高效,因为提前过滤数据 | 相对较低效,因为需先分组再过滤 |
| 适用场景 | 过滤满足条件的单条记录 | 过滤满足条件的分组(如"订单总数大于10的客户") |
| 是否必须与 GROUP BY 一起使用 | 否 | 是(除非使用聚合函数但无显式 GROUP BY,某些数据库允许) |
示例:显示那些合计薪水超过 13,000 的每个工作岗位的合计薪水。排除那些JOB_ID中含有REP的工作岗位,并且用合计月薪排序列表。
sql
SELECT job_id, SUM(salary) AS "Total Salary"
FROM employees
WHERE job_id NOT LIKE '%REP%'
GROUP BY job_id
HAVING SUM(salary) > 13000
ORDER BY "Total Salary";
子查询
假如要写一个查询来找出挣钱比 Abel 的薪水还多的人。为了解决这个问题,需要两个查询:一个找出 Abel 的收入,第二个查询找出收入高于 Abel 的人。可以用组合两个查询的方法解决这个问题。内查询或子查询返回一个值给外查询或主查询。使用一个子查询相当于执行两个连续查询并且用第一个查询的结果作为第二个查询的搜索值
子查询是一个 SELECT 语句(写在括号里),它是嵌在另一个 SELECT 语句中的子句。使用子查询可以用简单的语句构建功能强大的语句。可以将子查询放在许多的 SQL 子句中,包括:
- WHERE 子句
- HAVING 子句
- FROM 子句
示例:找出挣钱比 Abel 的薪水还多的人
sql
select last_name, salary
from employees
where salary > (
select salary
from employees
where last_name = 'Abel'
)
使用子查询的原则
- 子查询放在圆括号中。
- 将子查询放在比较条件的右边。
- 在单行子查询中用单行运算符,在多行子查询中用多行运算符。
单行子查询表示返回一条数据,多行子查询表示返回多条数据;所以主查询对应的运算符也得变化
示例:查询与Fox同一部门的同事,并显示名称和部门id
sql
select last_name, department_id
from employees
where department_id = (
select department_id
from employees
where last_name = 'Fox'
)
单行子查询
单行子查询是从内查询返回一行的查询。在该子查询类型中用一个单行操作符。
| 单行运算符 | 描述 |
|---|---|
| = | 等于 |
| > | 大于 |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| <> | 不等于 |
示例:查询与Fox同一部门的同事,并显示名称和部门id,但不包括Fox
sql
select e.last_name, e.department_id
from employees e
where e.last_name <> 'Fox' and e.department_id = (
select department_id
from employees
where last_name = 'Fox'
)
多行子查询
- 返回多于一行
- 使用多行比较符
| 操作 | 含义 |
|---|---|
| IN | 等于列表中的任何一个值 |
| ANY | 比较子查询返回的每个值 |
| ALL | 比较子查询返回的所有值 |
ANY运算符比较一个值与一个子查询返回的每一个值
<ANY意思是小于最大值>ANY意思是大于最小值
sql
select last_name, salary, employee_id, job_id
from employees
where salary < ANY (
select salary
from employees
where job_id = 'IT_PROG'
)
and job_id <> 'IT_PROG';
子查询中的空值
sql
select emp.last_name
from employees emp
where emp.employee_id not in (
select mgr.manager_id
from employees mgr
);
-- 会返回no rows selected
原因: 因为子查询返回的manager_id字段存在空值。而 任何值与 NULL 比较(包括 != NULL)的结果都是 UNKNOWN(既不是 TRUE 也不是 FALSE)。
由于 WHERE 子句只保留结果为 TRUE 的行,UNKNOWN 会被当作 FALSE 过滤掉。
因此,只要子查询结果中有任何一个 NULL,整个 NOT IN 条件就永远不成立,导致外层查询返回空结果 ------ 即 "no rows selected"。
注意,空值作为一个子查询结果集的一部分,如果使用 IN 操作符的话,不是一个问题。IN 操作符相当于 =ANY。
写一个查询显示所有工作在有任一雇员的名字中包含一个 u 的部门的雇员的雇员号和名字。
sql
select e.employee_id, e.last_name
from employees e
where e.department_id in (
select eu.department_id
from employees eu
where eu.last_name like '%u%'
);
索引
索引是对数据库表中的一列或多列值进行排序的一种结构,使用索引可以快速访问数据库表中的特定信息。索引是一种特殊的文件,它们包含着对数据表里所有记录的位置信息。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。MySQL 索引的建立对于MySQL 的高效运行是很重要的,索引可以大大提高 MySQL 的检索速度。
索引优点:
- 通过创建唯一性索引,可以保证数据库表中的每一行数据的唯一性;
- 可以加快数据的检索速度;
- 可以加速表与表之间的连接;
- 在使用分组和排序进行检索的时候,可以减少查询中分组和排序的时间;
索引缺点
- 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加;
- 索引需要占用物理空间,数据量越大,占用空间越大;
- 会降低表的增删改的效率,因为每次增删改索引都需要进行动态维护;
什么时候需要创建索引
- 频繁作为查询条件的字段应该创建索引;
- 查询中排序的字段创建索引将大大提高排序的速度(索引就是排序加快速查找);
- 查询中统计或者分组的字段;
什么时候不需要创建索引
- 频繁更新的字段不适合创建索引,因为每次更新不单单是更新记录,还会更新索引,保存索引文件;
- where条件里用不到的字段,不创建索引;
- 表记录太少,不需要创建索引;
- 经常增删改的表;
- 数据重复且分布平均的字段,因此为经常查询的和经常排序的字段建立索引。注意某些数据包含大量重复数据,因此他建立索引就没有太大的效果,例如性别字段,只有男女,不适合建立索引;
MySQL的索引类型
- 普通索引:最基本的索引,它没有任何限制。
- 唯一索引:索引列的值必须唯一,但允许有空值,如果是组合索引,则列值的组合必须唯一。
- 主键索引:特殊的索引,唯一的标识一条记录,不能为空,一般用primary key来约束。
- 联合索引:在多个字段上建立索引,能够加速查询到速度。
普通索引
是最基本的索引,它没有任何限制。在创建索引时,可以指定索引长度。length 为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度,如果是 BLOB 和 TEXT 类型,必须指定 length。
创建索引时需要注意:
- 如果指定单列索引长度,length 必须小于这个字段所允许的最大字符个数。
查询索引
sql
SHOW INDEX FROM 表名;
创建索引
sql
CREATE INDEX 索引名 ON 表名(列名(长度));
-- 也可以用alter语句创建索引
ALTER TABLE 表名 ADD INDEX 索引名(列名(长度));
示例为 emp表中的last_name字段创建索引
sql
create index emp_name_index on emp(last_name);
-- alter 语句
alter table emp add index emp_adress_index(adress);
show index from emp;
创建表时指定索引列
sql
CREATE TABLE `TABLE_NAME` (
COLUMN TYPE ,
PRIMARY KEY (`id`),
INDEX index_name (COLUMN(length))
);
示例:创建 emp4 表,包含 emp_id,name,address 列, 同时为 name 列创建索引 ,索引名为 emp4_name_index。
sql
create table emp4 (
emp_id int primary key auto_increment,
name varchar(20),
address varchar(20),
index emp4_name_index (name(10))
);
show index from emp4;
删除索引
sql
DROP INDEX 索引名 ON 表名;
drop index emp_name_index on emp;
唯一索引
唯一索引与普通索引类似,不同的就是: 索引列的值必须唯一,但允许有空值。
创建唯一索引
sql
CREATE UNIQUE INDEX 索引名 ON 表名(列名(长度));
当为某个列分配了唯一约束时,该列就已经是唯一索引了。
示例: 为 emp 表的 last_name 列创建唯一索引。
sql
create unique index emp_name_unique_index on emp(last_name);
主键索引
主键索引是特殊的唯一索引,一个表只能有一个主键,不允许有空值,一般是在创建表时指定。一旦指定了主键约束,就带有主键索引。 同时不能为一个没有分配主键约束的列创建主键索引。
sql
ALTER TABLE 表名 ADD PRIMARY KEY(列名);
组合索引
组合索引是指使用多个字段创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用(最左前缀原则)。
最左前缀原则
- 就是最左优先。
- 如: 我们使用表中的 name ,address ,salary 创建组合索引,那么想要组合索引生效, 我们只能使用如下组合:
- name/address/salary
- name/address
- name/
- 如果使用 addrees/salary 或者是 salary 则索引不会生效。
添加组合索引
sql
ALTER TABLE 表名 ADD INDEX 索引名(列名1,列名2);
示例: 为 emp 表的 last_name 和 address 列创建组合索引。
sql
alter table emp add index emp_name_address_index(last_name,address);
MySQL事务
事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
事务定义(Transaction)
- 事务是一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)
- 一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
- 事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
事务四大特征(ACID)
- 原子性(ATOMICITY):事务中的操作要么都不做,要么就全做。
- 一致性(CONSISTENCY):一个事务应该保护所有定义在数据上的不变的属性(例如完整性约束)。在完成了一个成功的事务时,数据应处于一致的状态。
- 隔离性(ISOLATION):一个事务的执行不能被其他事务干扰。
- 持久性(DURABILITY):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
事务类型
- 显式事务: 需要我们手动的提交或回滚。DML 语言中的所有操作都是显示事务操作。
- 隐式事务:数据库自动提交不需要我们做任何处理,同时也不具备回滚性。DDL、DCL 语言都是隐式事务操作
使用事务
| TCL语句 | 描述 |
|---|---|
| START TRANSACTION | 开启一个事务 |
| COMMIT | 提交一个事务 |
| ROLLBACK | 回滚一个事务 |
示例:
sql
-- 创建account 表,包含id、卡号、用户名、余额
create table account (
id int primary key auto_increment,
card_id varchar(20) not null,
username varchar(20) not null,
balance double(10,2)
);
-- 向account表中插入两条数据。
insert into account values (null,'1001','张三',2000);
insert into account values (null,'1002','李四',1000);
在一个事务中完成转账业务。
sql
start transaction;
update account set balance = balance - 100 where card_id = '1001';
update account set balance = balance + 100 where card_id = '1002';
commit;
事务的并发问题
脏读:一个事务读到另一个事务未提交的数据。
A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。
| 时间线 | A事务 | B事务 |
|---|---|---|
| 1 | 开始事务 | |
| 2 | 开始事务 | |
| 3 | 查询账户余额为2000元 | |
| 4 | 取款1000元,余额为1000元 | |
| 5 | 查询账户余额为1000元(产生脏读) | |
| 6 | 取款操作发生未知错误,事务回滚,余额变更为2000元 | |
| 7 | 转入2000元,余额被更改为3000元 | |
| 8 | 提交事务 |
但按照正常逻辑,现在的账户余额应该为4000元,而不是3000元。
不可重复读(前后多次读取,数据内容不一致)
在一个事务内读取表中的某一行数据,多次读取结果不同。事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。
| 时间线 | A事务 | B事务 |
|---|---|---|
| 1 | 开始事务 | |
| 2 | 第一次查询,小明年龄为20岁 | |
| 3 | 开始事务 | |
| 4 | 其他操作 | |
| 5 | 修改小明年龄为30岁 | |
| 6 | 提交事务 | |
| 7 | 第二次查询,小明年龄为30岁 |
按照正常逻辑,事务A两次读取的数据应该一致
幻读(前后多次读取,数据总量不一致)
是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。
事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。
| 时间线 | A事务 | B事务 |
|---|---|---|
| 1 | 开始事务 | |
| 2 | 第一次查询,数据总量为100 | |
| 3 | 开始事务 | |
| 4 | 其他操作 | |
| 5 | 新增100条数据 | |
| 6 | 提交事务 | |
| 7 | 第二次查询,数据总量为200 |
按照正常逻辑,事务A两次读取的数据总量应该一致
事务的隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
事务的隔离级别有四种,从低到高依次为:
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE (串行)
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | √ | √ | √ |
| READ COMMITTED | x | √ | √ |
| REPEATABLE READ | x | x | √ |
| SERIALIZABLE | x | x | x |
查看MySQL的隔离级别:
sql
SELECT @@transaction_isolation;
设置MySQL的隔离级别: 对当前的session有效
sql
set session transaction isolation level 隔离级别;
用户管理
MySQL 是一个多用户的数据库系统,按权限,用户可以分为两种:
- root 用户,超级管理员,和由 root 用户创建的普通用户。
sql
-- 创建用户
create user '用户名'@'主机名' identified by '密码';
-- 查看用户
select user,host from mysql.user;
权限管理
新用户创建完后是无法登陆的,需要分配权限。
sql
GRANT 权限 ON 数据库.表 TO 用户名@登录主机 IDENTIFIED BY "密码"
主机:
| 字段 | 含义 |
|---|---|
| % | 表示任意主机 |
| localhost | localhost 不会被解析成 IP 地址,直接通过 UNIXsocket 连接 |
| 127.0.0.1 | 127.0.0.1 会被解析成 IP 地址,通过 TCP/IP 连接,并且只能在本机访问 |
| ::1 | ::1 就是兼容支持 ipv6 的,表示同 ipv4 的 127.0.0.1 |
权限列表
| 权限 | 作用范围 | 作用 |
|---|---|---|
| ALL PRIVILEGES | 所有数据库和表 | 授予所有数据库和表的权限 |
| SELECT | 表、列 | 查询数据(选择行) |
| INSERT | 表、列 | 插入新数据(插入行) |
| UPDATE | 表、列 | 修改已有数据(更新行) |
| DELETE | 表 | 删除数据(删除行) |
| CREATE | 数据库、表 | 创建数据库或表 |
| DROP | 数据库、表 | 删除数据库或表 |
| ALTER | 表 | 修改表结构 |
| INDEX | 表 | 创建或删除索引 |
| REFERENCES | 表、列 | 创建外键约束(部分存储引擎支持) |
| EXECUTE | 存储过程、函数 | 执行存储过程或函数 |
| CREATE VIEW | 视图 | 创建视图 |
| SHOW VIEW | 视图 | 查看视图定义 |
| CREATE ROUTINE | 存储过程、函数 | 创建存储过程或函数 |
| ALTER ROUTINE | 存储过程、函数 | 修改或删除存储过程/函数 |
| CREATE TEMPORARY TABLES | 数据库 | 创建临时表 |
| LOCK TABLES | 数据库 | 锁定表 |
| RELOAD | 全局 | 执行 FLUSH 操作(如刷新日志、缓存等) |
| SHUTDOWN | 全局 | 关闭 MySQL 服务 |
| PROCESS | 全局 | 查看当前运行的线程(SHOW PROCESSLIST) |
| FILE | 全局 | 读写服务器上的文件(如 LOAD DATA INFILE) |
| GRANT OPTION | 数据库、表 | 授予其他用户权限 |
| REPLICATION CLIENT | 全局 | 查询主/从服务器位置信息 |
| REPLICATION SLAVE | 全局 | 用于从服务器连接主服务器进行复制 |
| USAGE | 全局 | 无权限(常用于创建用户但不授予权限) |
示例:为pdljmin用户分配只能查nouse库中test表的数据权限,并且只能在本机访问:
sql
grant select on nouse.test to 'pdljmin'@'localhost' identified by '123456';
给pdnbplus分配所有数据库和表的权限:
sql
grant all privileges on *.* to 'pdnbplus'@'localhost' identified by '123456';
刷新权限:
sql
flush privileges;
删除用户:
sql
drop user '用户名'@'主机名';
分页查询
MySQL的分页查询,分页查询的实现原理是:
- 在 MySQL 数据库中使用 LIMIT 子句进行分页查询。
- MySQL 分页中开始位置为 0。
- 分页子句在查询语句的最后侧。
sql
SELECT column_name
FROM table_name
WHERE condition
order by column_name ASC/DESC
LIMIT start, count;
示例:查询员工编号大于1000的编号、员工姓名、员工性别、员工手机号,并且分页显示,每页显示5条数据,第2页的数据:
sql
select emp_id, emp_name, emp_sex, emp_phone
from employee
where emp_id > 1000
order by emp_id asc
limit 5, 5;
start:开始位置(不是页数,而是行数),从0开始。