目录
DDL(Data Definition Languages、数据定义语言):
DML(Data Manipulation Language、数据操作语言):
PRIMARY KEY(主键约束):唯一约束+非空约束的组合:
DDL(Data Definition Languages、数据定义语言):
这些语句定义了不同的数据库、表、视图、索引等数据库对象,还可以用来创建、删除、修改数据库和数据表的结构。
- 主要的语句关键字包括`CREATE`、`DROP`、`ALTER`等。
=========================================================================
数据库与表的一些常规操作:
数据库:
创建:
CREATE DATABASE IF NOT EXISTS 数据库名 CHARACTER SET 字符集;(判断数据库是否已经存在,不存在则创建数据库,并指定字符集)
使用:
SHOW DATABASES; (查看当前所有的数据库)| SELECT DATABASE(); (查看当前所有的数据库)| USE 数据库名;(使用/切换数据库)
SHOW TABLES FROM 数据库名 (查看指定库下所有的表)| SHOW CREATE DATABASE 数据库名;(查看数据库的创建信息)
修改:
ALTER DATABASE 数据库名 CHARACTER SET 字符集;(更改数据库字符集)
删除:
DROP DATABASE IF EXISTS 数据库名;(删除指定的数据库)
表:
创建:
方式一:
CREATE TABLE [IF NOT EXISTS] 表名(
字段1, 数据类型 [约束条件] [默认值],
字段2, 数据类型 [约束条件] [默认值],
字段3, 数据类型 [约束条件] [默认值],
……
[表约束条件]
);
方式二:用查出来的数据填充一个表
CREATE TABLE emp1 AS SELECT * FROM employees;
使用:
SHOW CREATE TABLE 表名;(查看数据表结构)
修改:
RENAME TABLE 表名 TO 新表名;(重命名表)
ALTER TABLE 表名 ADD 字段名 字段类型 ;(追加一个列)
ALTER TABLE 表名 MODIFY 段名1 字段类型 [约束条件] 【DEFAULT 默认值】;(修改一个列)
ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型;(重命名一个列)
ALTER TABLE 表名 DROP 字段名;(删除一个列)
删除:
DROP TABLE [IF EXISTS] 数据表1 [, 数据表2, …, 数据表n];(删除表)
TRUNCATE TABLE detail_dept;(清空表)--不可以回滚
DELETE FROM emp2; (清空表)--可以回滚
=========================================================================
DML(Data Manipulation Language、数据操作语言):
用于添加、删除、更新和查询数据库记录,并检查数据完整性。
- 主要的语句关键字包括`INSERT`、`DELETE`、`UPDATE`、`SELECT`等。
- SELECT是SQL语言的基础,最为重要。
=========================================================================
一、SELECT:
DISTINCT:去重
SELECT DISTINCT department_id FROM employees;DISTINCT(去除重复行)
1. DISTINCT 需要放到所有列名的前面,如果写成`SELECT salary, DISTINCT department_id FROM employees`会报错。
2. DISTINCT 其实是对后面所有列名的组合进行去重,你能看到最后的结果是 74 条,因为这 74 个部门id不同,都有 salary 这个属性值。如果你想要看都有哪些不同的部门(department_id),只需要写`DISTINCT department_id`即可,后面不需要再加其他的列名了。
=========================================================================
二、运算符、
BETWEEN AND:SELECT D FROM TABLE WHERE C BETWEEN A AND B(当C大于或等于A,并且C小于或等于B时,结果为1,否则结果为0)
=========================================================================
三、多表查询:
内连接:
SELECT 字段列表FROM A表
INNER JOIN B表 ON 关联条件
WHERE 等其他子句;
外连接:
两个表在连接过程中除了返回满足连接条件的行以外,还返回主表中不满足条件的行。没有匹配的行时, 结果表中相应的列为空
左外连接:
则连接条件中左边的表也称为`主表`,右边的表称为`从表`
SELECT 字段列表
FROM A表 LEFT JOIN B表 ON 关联条件
WHERE 等其他子句;
右外连接:
则连接条件中右边的表也称为`主表`,左边的表称为`从表`
SELECT 字段列表
FROM A表 RIGHT JOIN B表 ON 关联条件
WHERE 等其他子句;
UNION:返回两个查询的结果集的并集,去除重复记录
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id>90;
=========================================================================
四、单行函数:
参数可以是一列或一个值,返回一个结果,可以嵌套
=========================================================================
五、聚合函数:
作用于一组数据,返回一个值,聚合函数不能嵌套调用
数值型数据:
AVG()、SUM()
任意数据类型:
MAX()、MIN()、COUNT()
GROUP BY:可以将表中的数据分成若干组
SELECT column, group_function(column)
FROM table
[WHERE condition]
[GROUP BY group_by_expression(可以为多个列)]
[WITH ROLLUP]所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。
[HAVING]
[ORDER BY column(可以为多个列)];
HAVING子句:类似Where,不能在 WHERE 子句中使用聚合函数,having中就可以,但是必须要跟 GROUP BY 一起使用
Where和Having的区别:
| | 优点 | 缺点
| WHERE | 先筛选数据再关联,执行效率高 | 不能使用分组中的计算函数进行筛选
| HAVING | 可以使用分组中的计算函数 | 在最后的结果集中进行筛选,执行效率较低
怎么使用:分组统计函数的条件用 HAVING,普通条件用 WHERE
=========================================================================
六、子查询:
- 子查询(内查询)在主查询之前一次执行完成。
- 子查询的结果被主查询(外查询)使用 。
注意事项
- 子查询要包含在括号内
- 将子查询放在比较条件的右侧
- 单行操作符对应单行子查询,多行操作符对应多行子查询
单行子查询比较操作符:=,>,>=,<,<=,!=
多行子查询比较操作符:IN(),ANY(),ALL()
相关子查询:
EXISTS:
- **如果在子查询中不存在满足条件的行:**
- 条件返回 FALSE
- 继续在子查询中查找
- **如果在子查询中存在满足条件的行:**
- 不在子查询中继续查找
- 条件返回 TRUE
NOT EXISTS:
- **如果在子查询中 全都 都不存在满足条件的行:**
- 条件返回 true
- **如果在子查询中存在满足条件的行:**
- 条件返回 false
例子:
SELECT department_id, department_name
FROM departments d
WHERE NOT EXISTS (SELECT 'X'
FROM employees
WHERE department_id = d.department_id);
注意:where之后直接接关键字,主查询中每一条记录都会进入子查询,如果子查询返回为ture就会把这条记录添加入结果集,然后循环每一条记录
=========================================================================
七、数据处理之增删改
增:
方式一、VALUSE方式添加
INSERT INTO table_name(column1 [, column2, …, columnn])
VALUES
(value1 [,value2, …, valuen]),
(value1 [,value2, …, valuen]),
……
(value1 [,value2, …, valuen]);
方式二、查询结果插入到表中
INSERT INTO 目标表名
(tar_column1 [, tar_column2, …, tar_columnn])
SELECT
(src_column1 [, src_column2, …, src_columnn])
FROM 源表名
[WHERE condition]
改:
UPDATE table_name
SET column1=value1, column2=value2, … , column=valuen
[WHERE condition]
删:
DELETE FROM table_name [WHERE <condition>];
=========================================================================
八、约束:
查看某个表已有的约束:
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)
SELECT * FROM information_schema.table_constraints
WHERE table_name = '表名称';
NOT NULL(非空约束):
非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空
(1)建表时:
CREATE TABLE 表名称(
字段名 数据类型,
字段名 数据类型 NOT NULL,
字段名 数据类型 NOT NULL
);
(2)建表后:
alter table 表名称 modify 字段名 数据类型 not null;
(3)删除非空约束:
alter table 表名称 modify 字段名 数据类型;(去掉not null,相当于修改某个非注解字段,该字段允许为空)
Key的作用:用于,创建、操作、提示索引
-------------------------------------------------------------------------------------------------------------------
UNIQUE(唯一性约束):
唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。
添加唯一性约束的列上也会自动创建唯一索引,删除唯一约束只能通过删除唯一索引的方式删除。(所以用UNIQUE,和UNIQUE KEY一个效果)
如果创建唯一约束时未指定名称,如果是单列,就默认和列名相同;如果是组合列,那么默认和()中排在第一个的列名相同。也可以自定义唯一性约束名。
(1)建表时:
create table 表名称(
字段名 数据类型,
字段名 数据类型 unique,
字段名 数据类型 unique key,
字段名 数据类型
);
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
[constraint 约束名] unique key(字段名)//可以为多个字段,表示组合唯一
);
(2)建表后指定唯一键约束:
#方式1:
alter table 表名称 add unique key(字段列表); //可以为多个字段,表示组合唯一
#方式2:
alter table 表名称 modify 字段名 字段类型 unique;
(3)删除唯一性索引:
ALTER TABLE 表名
DROP INDEX 索引名;
-------------------------------------------------------------------------------------------------------------------
PRIMARY KEY(主键约束):唯一约束+非空约束的组合:
一个表最多只能有一个主键约束,主键名总是PRIMARY,主键约束对应着表中的一列或者多列
如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复。
当创建主键约束时,系统默认会在所在的列或列组合上建立对应的**主键索引**(能够根据主键查询的,就根据主键查询,效率更高)。如果删除主键约束了,主键约束对应的索引就自动删除了。
需要注意的一点是,不要修改主键字段的值。因为主键是数据记录的唯一标识,如果修改了主键的值,就有可能会破坏数据的完整性。
添加主键约束:
(1)建表时指定主键约束:
create table 表名称(
字段名 数据类型 primary key, #列级模式
字段名 数据类型,
字段名 数据类型
);
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
[constraint 约束名] primary key(字段名) #表级模式 可以为多个字段,表示组合主键
);
(2)建表后增加主键约束:
ALTER TABLE 表名称 ADD PRIMARY KEY(字段列表);可以为多个字段,表示组合主键
(3)删除主键约束:
ALTER TABLE 表名称 DROP PRIMARY KEY;
-------------------------------------------------------------------------------------------------------------------
AUTO_INCREMENT(自增列):
一个表最多只能有一个自增长列
自增长列约束的列必须是键列(主键列,唯一键列),自增约束的列的数据类型必须是整数类型
如果自增列指定了 0 和 null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值。
(1)建表时:
create table 表名称(
字段名 数据类型 primary key auto_increment,
字段名 数据类型 unique key not null,
字段名 数据类型 unique key,
字段名 数据类型 not null default 默认值,
);
create table 表名称(
字段名 数据类型 default 默认值 ,
字段名 数据类型 unique key auto_increment,
字段名 数据类型 not null default 默认值,,
primary key(字段名)
);
(2)建表后:
alter table 表名称 modify 字段名 数据类型 auto_increment;
(3)删除自增约束:
alter table 表名称 modify 字段名 数据类型; (去掉auto_increment相当于删除)
-------------------------------------------------------------------------------------------------------------------
FOREIGN KEY(外键):
在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如 student_ibfk_1;),也可以指定外键约束名。
从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致。如果类型不一样,创建子表时,就会出现错误“ERROR 1005 (HY000): Can't create table'database.tablename'(errno: 150)”。
当创建外键约束时,系统默认会在所在的列上建立对应的普通索引,但是索引名是外键的约束名(删除外键约束后,必须`手动`删除对应的索引)
约束关系是针对双方的:
* 添加了外键约束后,主表的修改和删除数据受约束
* 添加了外键约束后,从表的添加和修改数据受约束
* 在从表上建立外键,要求主表必须存在
* 删除主表时,要求从表从表先删除,或将从表中外键引用该主表的关系先删除
(1)建表时:
create table 主表名称(
字段1 数据类型 primary key,
字段2 数据类型
);
create table 从表名称(
字段1 数据类型 primary key,
字段2 数据类型,
[CONSTRAINT <外键约束名称>] FOREIGN KEY(从表的某个字段) REFERENCES 主表名(被参考字段)
);
(2)建表后:
ALTER TABLE 从表名 ADD [CONSTRAINT 约束名] FOREIGN KEY (从表的字段) REFERENCES 主表名(被引用字段) [on update xx][on delete xx];
(3)删除外键约束:
第一步先查看约束名和删除外键约束
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个表的约束名
ALTER TABLE 从表名 DROP FOREIGN KEY 外键约束名;
第二步查看索引名和删除索引。(注意,只能手动删除)
SHOW INDEX FROM 表名称; #查看某个表的索引名
ALTER TABLE 从表名 DROP INDEX 索引名;
(4)约束等级:
Cascade方式(级 联):在父表上update/delete记录时,同步update/delete掉子表的匹配记录
Set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
No action(Restrict方式)默认方式:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
Set default方式(在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别
最佳方案:ON UPDATE CASCADE ON DELETE RESTRICT
例子: foreign key (deptid) references dept(did) ON UPDATE CASCADE ON DELETE RESTRICT
-------------------------------------------------------------------------------------------------------------------
DEFAULT(默认值)
给某个字段/某列指定默认值,一旦设置默认值,在插入数据时,如果此字段没有显式赋值,则赋值为默认值。
说明:默认值约束一般不在唯一键和主键列上加
(1)建表时:
create table 表名称(
字段名 数据类型 primary key,
字段名 数据类型 unique key not null,
字段名 数据类型 unique key,
字段名 数据类型 not null default 默认值,
);
(2)建表后:
alter table 表名称 modify 字段名 数据类型 default 默认值;
#如果这个字段原来有非空约束,你还保留非空约束,那么在加默认值约束时,还得保留非空约束,否则非空约束就被删除了
#同理,在给某个字段加非空约束也一样,如果这个字段原来有默认值约束,你想保留,也要在modify语句中保留默认值约束,否则就删除了
alter table 表名称 modify 字段名 数据类型 default 默认值 not null;
(3)删除默认值约束:
alter table 表名称 modify 字段名 数据类型 ;#删除默认值约束,也不保留非空约束
alter table 表名称 modify 字段名 数据类型 not null; #删除默认值约束,保留非空约束
总结:
modify可以用来删除,非空约束,自增列约束,默认值约束;
但是无法删除,唯一性约束,主键约束,外键约束(因为删除他们主要是要删除他们的索引,且外键约束属于表级约束,也不在modify的作用范围内了)
=========================================================================
九、视图:
视图:视图是一种`虚拟表`,本身是`不具有数据`的,占用很少的内存空间,它是 SQL 中的一个重要概念。
视图建立在已有表的基础上, 视图赖以建立的这些表称为基表
视图的创建和删除只影响视图本身,不影响对应的基表。但是当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化,反之亦然。
向视图提供数据内容的语句为 SELECT 语句, 可以将视图理解为存储起来的(SELECT语句的结果集 )
在创建视图时,没有在视图名后面指定字段列表,则视图中字段列表默认和SELECT语句中的字段列表一致。如果SELECT语句中给字段取了别名,那么视图中的字段名和别名相同。
创建视图:
CREATE VIEW 视图名称
AS 查询语句(可以基于单表、多表、视图)
查看视图:
SHOW TABLES;(查看数据库的表对象、视图对象)
DESC / DESCRIBE 视图名称;(查看视图的结构)
=========================================================================
十、存储过程与函数:
存储过程:
语法分析:
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
//声明什么语句组成(mysql),执行结果是否确定(传入x,一定得到y,不管我试几次),使用语句的权限(读写),执行此函数的权限
COMMENT 'string'(注释信息)
BEGIN
存储过程体
END
解释:
1、参数前面的符号的意思
- `IN`:当前参数为输入参数,也就是表示入参;
存储过程只是读取这个参数的值。如果没有定义参数种类,`默认就是 IN`,表示输入参数。
- `OUT`:当前参数为输出参数,也就是表示出参;
执行完成之后,调用这个存储过程的客户端或者应用程序就可以读取这个参数返回的值了。
- `INOUT`:当前参数既可以为输入参数,也可以为输出参数。
2、形参类型可以是 MySQL数据库中的任意类型。
3、多条语句
1. BEGIN…END:BEGIN…END 中间包含了多个语句,每个语句都以(;)号为结束符。
2. DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 语句中间,而且需要在其他语句使用之前进行变量的声明。
3. SET:赋值语句,用于对变量进行赋值。
4. SELECT… INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。
5. DELIMITER 新的结束标记
调用存储过程:
CALL 存储过程名(实参列表)
1、调用in模式的参数:
CALL sp1('值');
2、调用out模式的参数:
SET @name;
CALL sp1(@name);
SELECT @name;
3、调用inout模式的参数:
SET @name=值;
CALL sp1(@name);
SELECT @name;
例子:
DELIMITER //
CREATE PROCEDURE show_someone_salary2(IN empname VARCHAR(20),OUT empsalary DOUBLE)
BEGIN
SELECT salary INTO empsalary FROM emps WHERE ename = empname;
END //
DELIMITER ;
-------------------------------------------------------------------------------------------------------------------
存储函数:
语法分析:
CREATE FUNCTION 函数名(参数名 参数类型,...) //参数只能是IN
RETURNS 返回值类型 //函数体必须包含一个`RETURN value`语句
[characteristics ...]//声明,什么语句组成,执行结果是否确定,使用语句的权限,执行此函数的权限
COMMENT 'string'(注释信息)
BEGIN
函数体 #函数体中肯定有 RETURN 语句
END
调用存储函数:
SELECT 函数名(实参列表)
查看:
SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']
修改:
ALTER {PROCEDURE | FUNCTION} 存储过程或函数的名 [characteristic ...]
删除:
DROP {PROCEDURE | FUNCTION} [IF EXISTS] 存储过程或函数的名
4.4 对比存储函数和存储过程
关键字 | 调用语法 | 返回值 | 应用场景 | |
---|---|---|---|---|
存储过程 | PROCEDURE | CALL 存储过程() | 理解为有0个或多个 | 一般用于更新 |
存储函数 | FUNCTION | SELECT 函数() | 只能是一个 | 一般用于查询结果为一个值并返回时 |
此外,存储函数可以放在查询语句中使用,存储过程不行。反之,存储过程的功能更加强大,包括能够执行对表的操作(比如创建表,删除表等)和事务操作,这些功能是存储函数不具备的。
=========================================================================
十一、变量:
系统变量:
变量由系统定义,不是用户定义,属于`服务器`层面。
查看:
#查看满足条件的部分全局系统变量。(全局变量)
SHOW GLOBAL VARIABLES LIKE '%标识符%';
#查看满足条件的部分会话变量。(local变量)
SHOW SESSION VARIABLES LIKE '%标识符%';
#查看指定的全局系统变量的值
SELECT @@global.变量名;
#查看指定的会话变量的值
SELECT @@session.变量名;
修改:
#为某个全局系统变量赋值
SET GLOBAL 变量名=变量值;
#为某个会话变量赋值
SET SESSION 变量名=变量值;
用户变量:
用户变量是用户自己定义的,作为 MySQL 编码规范,MySQL 中的用户变量以`一个“@”`开头。
会话用户变量:
作用域和会话变量一样,只对`当前连接`会话有效
定义:SET @用户变量 = 值;(方式一) SELECT 表达式 INTO @用户变量 [FROM 等子句];(方式二)
查看:SELECT @用户变量
局部变量:
只在 BEGIN 和 END 语句块中有效。局部变量只能在`存储过程和函数`中使用。
定义:DECLARE 变量名 类型 [default 值]; # 如果没有DEFAULT子句,初始值为NULL
变量赋值:SET 变量名=值;(方式一) SELECT 字段名或表达式 INTO 变量名 FROM 表;(方式二)
查看:SELECT 局部变量名;
对比会话用户变量与局部变量:
作用域 | 定义位置 | 语法 | |
会话用户变量 | 当前会话 | 会话的任何地方 | 加@符号,不用指定类型 |
局部变量 | 定义它的BEGIN END中 | BEGIN END的第一句话 | 一般不用加@,需要指定类型 |
=========================================================================
十二、定义条件与处理程序(try cath):
定义条件:
DECLARE 错误名称 CONDITION FOR(SQLSTATE) 错误码(或错误条件)
定义处理程序:
DECLARE 处理方式 HANDLER FOR 错误类型 处理语句
处理方式:
- `CONTINUE`:表示遇到错误不处理,继续执行。
- `EXIT`:表示遇到错误马上退出。
错误类型:
- `SQLSTATE '字符串错误码'`:表示长度为5的sqlstate_value类型的错误代码;
- `MySQL_error_code`:匹配数值类型错误代码;
- `错误名称`:表示DECLARE ... CONDITION定义的错误条件名称。
- `SQLWARNING`:匹配所有以01开头的SQLSTATE错误代码;
- `NOT FOUND`:匹配所有以02开头的SQLSTATE错误代码;
- `SQLEXCEPTION`:匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码;
例子:
DELIMITER //
CREATE PROCEDURE UpdateDataNoCondition()
BEGIN
#定义处理程序
DECLARE CONTINUE HANDLER FOR 1048 SET @proc_value = -1;
SET @x = 1;
UPDATE employees SET email = NULL WHERE last_name = 'Abel';
SET @x = 2;
UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
SET @x = 3;
END //
DELIMITER ;
结果:@x=3,@proc_value=-1
=========================================================================
十三、流程控制:
分支结构之 IF:
① 不同的表达式对应不同的操作 ② 使用在begin end中
IF 表达式1 THEN 操作1
[ELSEIF 表达式2 THEN 操作2]……
[ELSE 操作N]
END IF
分支结构之 CASE:
#情况一:类似于switch:
CASE 表达式
WHEN 值1 THEN 结果1或语句1(如果是语句,需要加分号)
WHEN 值2 THEN 结果2或语句2(如果是语句,需要加分号)
...
ELSE 结果n或语句n(如果是语句,需要加分号)
END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
#情况二:类似于多重if:
CASE
WHEN 条件1 THEN 结果1或语句1(如果是语句,需要加分号)
WHEN 条件2 THEN 结果2或语句2(如果是语句,需要加分号)
...
ELSE 结果n或语句n(如果是语句,需要加分号)
END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
循环结构之LOOP:while(true):
[loop_label:] LOOP
循环执行的语句
(使用ITERATE子句),跳过本次循环。(类似continue;)
(使用LEAVE子句),跳出循环过程或者跳出程序体。(类似于break;)
END LOOP [loop_label]
循环结构之WHILE:whle(条件):
[while_label:] WHILE 循环条件 DO
循环体
END WHILE [while_label];
循环结构之REPEAT:do while(条件):
[repeat_label:] REPEAT
循环体的语句
UNTIL 结束循环的条件表达式
END REPEAT [repeat_label]
例子:跳出程序体
begin_label: BEGIN
IF num<=0 THEN LEAVE begin_label;
END IF;
END //
例子:跳出循环过程
add_loop:LOOP
SET id = id +1;
IF id >= 10 THEN LEAVE add_loop;
END IF;
END LOOP add_loop;
=========================================================================
十四、游标:
声明顺序:变量-条件-游标-处理程序
游标可以在存储过程和函数中使用
游标充当了指针的作用
声明游标:
DECLARE cursor_name(游标名字) CURSOR FOR select_statement(SELECT 语句);
要使用 SELECT 语句来获取数据结果集,而此时还没有开始遍历数据,这里 select_statement 代表的是 SELECT 语句,返回一个用于创建游标的结果集。
打开游标:
OPEN cursor_name(游标名字)
当我们定义好游标之后,如果想要使用游标,必须先打开游标。打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区,为后面游标的`逐条读取`结果集中的记录做准备。
使用游标:
FETCH cursor_name(游标名字) INTO var_name [, var_name] ...
这句的作用是使用 cursor_name 这个游标来读取当前行,并且将数据保存到 var_name 这个变量中,游标指针指到下一行。如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。
注意:游标的查询结果集中的字段数,必须跟 INTO 后面的变量数一致
关闭游标:
CLOSE cursor_name(游标名字)
因为游标会`占用系统资源`,如果不及时关闭,**游标会一直保持到存储过程结束**,影响系统运行的效率。而关闭游标的操作,会释放游标占用的系统资源。
=========================================================================
十五、触发器:
触发器:由`事件来触发`某个操作,这些事件包括`INSERT`、`UPDATE`、`DELETE`事件
当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,可以使用触发器来实现。
创建:
CREATE TRIGGER 触发器名称
{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名
FOR EACH ROW
触发器执行的语句块;
- `表名`:表示触发器监控的对象。
- `BEFORE|AFTER`:表示触发的时间。BEFORE 表示在事件之前触发;AFTER 表示在事件之后触发。
- `INSERT|UPDATE|DELETE`:表示触发的事件。
- INSERT 表示插入记录时触发;
- UPDATE 表示更新记录时触发;
- DELETE 表示删除记录时触发。
- `触发器执行的语句块`:可以是单条SQL语句,也可以是由BEGIN…END结构组成的复合语句块。
查看:
SHOW TRIGGERS(所有触发器)
SHOW CREATE TRIGGER 触发器名(某个触发器)
删除:
DROP TRIGGER IF EXISTS 触发器名称;
注意,如果在子表中定义了外键约束,并且外键指定了ON UPDATE/DELETE CASCADE/SET NULL子句,此时修改父表被引用的键值或删除父表被引用的记录行时,也会引起子表的修改和删除操作,此时基于子表的UPDATE和DELETE语句定义的触发器并不会被激活。
=========================================================================
注意:
1、所有运算符或列值遇到null值,运算的结果都为null
2、遇到要使用关键字作为名字可以用着重号``
3、LIMIT子句必须放在整个SELECT语句的最后
4、group by,中在SELECT列表中所有未包含在组函数中的列都应该包含在 GROUP BY子句中(列名要不然在聚合函数中,要不然就是以该列名分组的)
=========================================================================
关键字顺序:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...
执行顺序:FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT
执行原理:
SELECT 是先执行 FROM 这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤:
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个`虚拟表`,然后将这个虚拟表传入下一个步骤中作为输入。需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。
1. 首先先通过 CROSS JOIN 求笛卡尔积,相当于得到虚拟表 vt(virtual table)1-1;
2. 通过 ON 进行筛选,在虚拟表 vt1-1 的基础上进行筛选,得到虚拟表 vt1-2;
3. 添加外部行。如果我们使用的是左连接、右链接或者全连接,就会涉及到外部行,也就是在虚拟表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3。
当然如果我们操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得到是我们的原始数据。
当我们拿到了查询数据表的原始数据,也就是最终的虚拟表 `vt1`,就可以在此基础上再进行 `WHERE 阶段`。在这个阶段中,会根据 vt1 表的结果进行筛选过滤,得到虚拟表 `vt2`。
然后进入第三步和第四步,也就是 `GROUP 和 HAVING 阶段`。在这个阶段中,实际上是在虚拟表 vt2 的基础上进行分组和分组过滤,得到中间的虚拟表 `vt3` 和 `vt4`。
当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 `SELECT 和 DISTINCT 阶段`。
首先在 SELECT 阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表 `vt5-1` 和 `vt5-2`。
当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 `ORDER BY 阶段`,得到虚拟表 `vt6`。
最后在 vt6 的基础上,取出指定行的记录,也就是 `LIMIT 阶段`,得到最终的结果,对应的是虚拟表 `vt7`。
当然我们在写 SELECT 语句的时候,不一定存在所有的关键字,相应的阶段就会省略。
同时因为 SQL 是一门类似英语的结构化查询语言,所以我们在写 SELECT 语句的时候,还要注意相应的关键字顺序,所谓底层运行的原理,就是我们刚才讲到的执行顺序。
=========================================================================
新特性:
a列值为1、b列值为2,c列不需要手动插入,定义a+b的结果为c的值
CREATE TABLE tb1(
id INT,
a INT,
b INT,
c INT GENERATED ALWAYS AS (a + b) VIRTUAL
);