MySQL实战系列3:视图、存储过程、函数、索引全解析
作者介绍
索宁,擅长Python开发、MySQL、前端等众多技术领域,曾负责众多企业安全架构解决方案 ,涉猎行业有媒体、出版社、航空运输、医疗、军队、政府、教育等。
一、视图
视图是查询命令结果构成的一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集合,并可以当作表来查询使用。
--格式:CREATE VIEW 视图名称 AS SQL语句
CREATE VIEW v1 AS
SELET nid,
name
FROM
A
WHERE
nid > 4
--格式:DROP VIEW 视图名称
DROP VIEW v1
-- 格式:ALTER VIEW 视图名称 AS SQL语句
ALTER VIEW v1 AS
SELET A.nid,
B. NAME
FROM
A
LEFT JOIN B ON A.id = B.nid
LEFT JOIN C ON A.id = C.nid
WHERE
A.id > 2
AND C.nid < 5
视图的使用和普通表一样,由于视图是虚拟表,所以无法对其真实表进行创建、更新和删除操作,仅做查询用。
select * from v1
二、触发器
对某个表进行【增/删/改】操作的前后触发一些操作即为触发器,如果希望触发增删改的行为之前或之后做操作时,可以使用触发器,触发器用于自定义用户对表的行进行【增/删/改】前后的行为。
# 插入前
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
# 插入后
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
# 删除前
CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 删除后
CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新前
CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新后
CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
创建触发器基本语法是以下code:
但有一点要注意,触发器内关键字NEW表示即将插入的数据行,OLD表示即将删除的数据行。
# 插入前触发器
delimiter //
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
IF NEW. NAME == 'nick' THEN
INSERT INTO tb2 (NAME)
VALUES
('aa')
END
END//
delimiter ;
# 插入后触发器
delimiter //
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
IF NEW. num = 666 THEN
INSERT INTO tb2 (NAME)
VALUES
('666'),
('666') ;
ELSEIF NEW. num = 555 THEN
INSERT INTO tb2 (NAME)
VALUES
('555'),
('555') ;
END IF;
END//
delimiter
DROP TRIGGER tri_after_insert_tb1;
触发器是由于对表的增、删、改操作时被动执行的。
三、存储过程
存储过程是一个SQL语句集合,类似函数,需要主动调用。
# 无参数存储过程
# 创建存储过程
delimiter //
create procedure p1()
BEGIN
select * from t1;
END//
delimiter ;
# 执行存储过程
call p1()
都说了类似函数,那必须得可以接收参数,且参数有三类:
in 仅用于传入参数用
out 仅用于返回值用
inout 既可以传入又可以当作返回值
# 有参数存储过程
# 创建存储过程
delimiter \\ # 结尾分号改为\\
create procedure p1(
in i1 int,
in i2 int,
inout i3 int,
out r1 int
)
BEGIN
DECLARE temp1 int; # 创建申明局部变量
DECLARE temp2 int default 0;
set temp1 = 1;
set r1 = i1 + i2 + temp1 + temp2;
set i3 = i3 + 100;
end\\
delimiter ;
# 执行存储过程
DECLARE @t1 INT default 3;
DECLARE @t2 INT;
CALL p1 (1, 2 ,@t1, @t2);
SELECT @t1,@t2;
执行为函数名加括号;
DECLARE代表创建一个局部变量
# 无参数
call proc_name()
# 有参数,全in
call proc_name(1,2)
# 有参数,有in,out,inout
DECLARE @t1 INT;
DECLARE @t2 INT default 3;
call proc_name(1,2,@t1,@t2)
pymysql执行存储过程
四、函数
该讲讲真正的函数了,与存储过程不同的是有return值。
delimiter \\
create function f1(
i1 int,
i2 int)
returns int
BEGIN
declare num int;
set num = i1 + i2;
return(num);
END \\
delimiter ;
drop function func_name;
# 获取返回值
declare @i VARCHAR(32);
select UPPER('nick') into @i;
SELECT @i;
# 在查询中使用
select f1(11,nid) ,name from tb2;
当然MySQL也不例外,有着非常好用的内置函数。
五、事务
事务用于将某些操作的多个SQL语句作为原子性操作,只有出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
delimiter 为改变结束符号,默认";"
# 支持事务的存储过程
delimiter \\
create PROCEDURE p1(
OUT p_return_code tinyint
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
set p_return_code = 2;
rollback;
END;
START TRANSACTION;
DELETE from tb1;
insert into tb2(name)values('seven');
COMMIT;
-- SUCCESS
set p_return_code = 0;
END\\
delimiter ;
# 执行存储过程
DECLARE @i TINYINT;
call p1(@i);
select @i;
六、索引
索引是表的索引目录,在查找内容之前先查目录中查找索引位置,从而快速定位查询数据;
可以理解成新华字典中的索引;
索引会保存在额外的文件中。
一般的索引种类及功能:
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一 + 表中只有一个(不可以有null)
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索
索引合并:使用多个单列索引组合查询搜索。
覆盖索引:select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。
a、普通索引
# 创建表 + 索引
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
index ix_name (name)
)
# 创建索引
create index index_name on table_name(column_name)
# 删除索引
drop index_name on table_name;
# 查看索引
show index from table_name;
#注意:对于创建索引时如果是BLOB 和 TEXT 类型,必须指定length。
create index ix_extra on in1(extra(32));
b、唯一索引
# 创建表 + 唯一索引
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
unique ix_name (name)
)
# 创建唯一索引
create unique index 索引名 on 表名(列名)
# 删除唯一索引
drop unique index 索引名 on 表名
c、主键索引
# 创建表 + 创建主键
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
index ix_name (name)
)
OR
create table in1(
nid int not null auto_increment,
name varchar(32) not null,
email varchar(64) not null,
extra text,
primary key(ni1),
index ix_name (name)
)
# 创建主键
alter table 表名 add primary key(列名);
# 删除主键
alter table 表名 drop primary key;
alter table 表名 modify 列名 int, drop primary key;
d、组合索引
组合索引是多个列组合成一个索引来查询。
应用场景:频繁的同时使用多列来进行查询,如:where name = 'nick' and age = 18。
# 创建表
create table mess(
nid int not null auto_increment primary key,
name varchar(32) not null,
age int not null
)
# 创建组合索引
create index ix_name_age on mess(name,age);
如上创建组合索引之后,查询一定要注意:
name and email -- >使用索引,name一定要放前面
name -- >使用索引
email -- >不使用索引
注意:同时搜索多个条件时,组合索引的性能效率好过于多个单一索引合并。
# 查看索引
show index from 表名
# 查看执行时间
set profiling = 1; # 开启profiling
SQL... # 执行SQL语句
show profiles; # 查看结果
# like '%xx',避免%_写在开头
select * from tb1 where name like '%n';
# 使用函数
select * from tb1 where reverse(name) = 'nick';
# or
select * from tb1 where nid = 1 or email = '630571017@qq.com';
注:当or条件中有未建立索引的列才失效,否则会走索引
# 类型不一致
如果列是字符串类型,传入条件是必须用引号引起来。
select * from tb1 where name = 999;
# !=,不等于
select * from tb1 where name != 'nick'
注:如果是主键,则还是会走索引
select * from tb1 where nid != 123
# >,大于
select * from tb1 where name > 'nick'
注:如果是主键或索引是整数类型,则还是会走索引
select * from tb1 where nid > 123
select * from tb1 where num > 123
# order by
select email from tb1 order by name desc;
当根据索引排序时候,选择的映射如果不是索引,则不走索引
注:如果对主键排序,则还是走索引:
select * from tb1 order by nid desc;
# 组合索引最左前缀
如果组合索引为:(name,email),查询使用:
name and email -- 使用索引
name -- 使用索引
email -- 不使用索引
# 避免使用select *
# count(1)或count(列) 代替 count(*)
# 创建表时尽量时 char 代替 varchar
# 表的字段顺序固定长度的字段优先
# 组合索引代替多个单列索引(经常使用多个条件查询时)
# 尽量使用短索引
# 使用连接(JOIN)来代替子查询(Sub-Queries)
# 连表时注意条件类型需一致
# 索引散列值(重复少)不适合建索引,例:性别不适合
explain + 查询SQL 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化。
id
查询顺序标识
特别的:如果使用union连接气值可能为null
日志可以记录哪些查询语句慢,没有走索引,用户发现问题,从而解决优化
a、配置MySQL自动记录慢日志
修改配置文件 my.ini,没有则添加
slow_query_log = OFF # 是否开启慢日志记录
long_query_time = 2 # 时间限制,超过此时间,则记录
slow_query_log_file = /usr/slow.log # 日志文件
log_queries_not_using_indexes = OFF # 为使用索引的搜索是否记录
# 查看当前配置信息:
show variables like '%query%'
# 修改当前配置:
set global 变量名 = 值
b、查看MySQL慢日志
七、其它
自然所有的东西都离不开基本的语法语句啦,循环语句与判断语句
if then\ elseif then\ else\ end if
# if条件语句
delimiter \\
CREATE PROCEDURE proc_if ()
BEGIN
declare i int default 0;
if i = 1 THEN
SELECT 1;
ELSEIF i = 2 THEN
SELECT 2;
ELSE
SELECT 7;
END IF;
END\\
delimiter ;
分为三种循环语句:while循环、repeat循环、loop循环。
# while循环
delimiter \\
CREATE PROCEDURE proc_while ()
BEGIN
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
SELECT
num ;
SET num = num + 1 ;
END WHILE ;
END\\
delimiter ;
# repeat循环,相当于 do while
delimiter \\
CREATE PROCEDURE proc_repeat ()
BEGIN
DECLARE i INT ;
SET i = 0 ;
repeat
select i;
set i = i + 1;
until i >= 5
end repeat;
END\\
delimiter ;
# loop
delimiter \\
CREATE PROCEDURE proc_loop ()
BEGIN
declare i int default 0;
loop_label: loop
select i;
set i=i+1;
if i>=5 then
leave loop_label;
end if;
end loop;
END\\
delimiter ;
# 动态执行SQL
delimiter \\
DROP PROCEDURE IF EXISTS proc_sql \\
CREATE PROCEDURE proc_sql ()
BEGIN
declare p1 int;
set p1 = 11;
set @p1 = p1;
PREPARE prod FROM 'select * from tb2 where nid > ?';
EXECUTE prod USING @p1;
DEALLOCATE prepare prod;
END\\
delimiter ;
# 方式二
delimiter \\
DROP PROCEDURE IF EXISTS proc_sql \\ # 如果不存在则添加,否则不做操作
CREATE PROCEDURE proc_sql (
in strSQL char(128),
in nidd int
)
BEGIN
set @sqll = strSQL;
set @p1 = nidd;
PREPARE prod FROM @sqll;
EXECUTE prod USING @p1;
DEALLOCATE prepare prod;
END\\
delimiter ;
call proc_sql('select * from suoning2 where id > ?','2');
相关专题:
◆ 近期热文 ◆
解密区块链:从基础概念到技术实现的进阶攻略
SQL优化三板斧:精简之道、驱动为王、集合为本
Oracle高并发系列2:事务日志写入引发的Redo log风波
运维改革探索(二):构建可视化分布式运维手段
一次非典型SQL优化:如何通过业务逻辑优化另辟蹊径?
◆ 专家专栏 ◆
◆ 近期活动 ◆