mysql索引和函数及存储过程

mysql索引和函数及存储过程

    • 1.索引
      • 1.1索引原理
        • 1.1.1 非聚簇索引(mysiam引擎)
        • 1.1.2 聚簇索引(innodb引擎)
      • 1.2常见索引
        • 1.2.1主键和联合主键索引
        • 1.2.2唯一和联合索引
        • 1.2.3 索引和联合索引
      • 1.3操作表
      • 1.4执行计划
    • 2.函数
      • 2.1内置函数
      • 2.2自定义函数
    • 3.存储过程
      • 3.1参数类型
      • 3.2返回值&结果集
      • 3.3事务&异常
      • 3.4游标
    • 4视图
    • 5.触发器

1.索引

核心作用:加速查找

在开发中会为那些经常会被搜索的列创建索引,以提高程序的响应速度,例如:手机号,邮箱,用户名…

1.1索引原理

索引底层是基于B+Tree的数据结构存储的
mysql索引和函数及存储过程_第1张图片
mysql索引和函数及存储过程_第2张图片

B+Tree结构连接:https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

数据库的索引基于B+Tree的数据结构实现,但在创建数据库时,如果指定不同的引擎,底层使用的B+Tree结构的原理有些不同

  • myisam引擎,非聚簇索引(数据和索引结构分开存储)
  • innodb引擎,聚簇索引(数据的主键索引结构存储在一起)

1.1.1 非聚簇索引(mysiam引擎)

数据和索引结构分开存储

create table 表名(
    ...
)engine=mysiam default charset=utf8;

mysql索引和函数及存储过程_第3张图片
mysql索引和函数及存储过程_第4张图片

1.1.2 聚簇索引(innodb引擎)

数据的主键索引结构存储在一起

create table 表名(
    ...
)engine=innodb default charset=utf8;

mysql索引和函数及存储过程_第5张图片
mysql索引和函数及存储过程_第6张图片

在mysql文件存储的体现

root@192 userdb # pwd
/usr/local/mysql/data/userdb
root@192 userdb # ls -l
total 1412928
-- innodb
-rw-r-----  1 _mysql  _mysql       8684 May 15 22:51 big.frm,表结构。
-rw-r-----  1 _mysql  _mysql  717225984 May 15 22:51 big.ibd,数据和索引结构。
-rw-r-----  1 _mysql  _mysql       8588 May 16 11:38 goods.frm
-rw-r-----  1 _mysql  _mysql      98304 May 16 11:39 goods.ibd
-- mysiam
-rw-r-----  1 _mysql  _mysql       8586 May 26 10:57 t2.frm,表结构
-rw-r-----  1 _mysql  _mysql          0 May 26 10:57 t2.MYD,数据
-rw-r-----  1 _mysql  _mysql       1024 May 26 10:57 t2.MYI,索引结构

企业中一般用innodb引擎(内部支持事务,行级锁,外键等特点),mysql5.5版本后默认是innodb

-- 显示表的创建信息
show create table users \G;

1.2常见索引

innodb引擎下,常见的索引类型:

  • 主键索引:加速查找,不能为空,+联合主键索引
  • 唯一索引:加速查找,不能重复,+联合唯一索引
  • 普通索引:加速查找,+联合索引

1.2.1主键和联合主键索引

  • 主键索引

    -- 方式一
    create table 表名(
    	id int not null auto_increment primary key,
        name varchar(32) not null
    )
    -- 方式二
    create table 表名(
    	id int not null auto_increment,
        name varchar(32) not null,
        primary key(id)
    )
    
  • 联合主键索引

    create table 表名(
    	id int not null auto_increment,
        name varchar(32) not null,
        primary key(id,name)
    )
    
  • 创建表之后添加主键索引

    alter table 表名 add primary key(列名)
    
  • 删除索引

    alter table 表名 drop primary key;
    

    注意:删除索引可能报错,自增列必须定义为键

    ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
    
    -- 解决方式,用下面方法删除
    alter table 表 change id id int not null;
    
    

1.2.2唯一和联合索引

  • 唯一索引

    create table 表名(
    	id int not null auto_increment,
        name varchar(32) not null,
        -- ix_email是唯一索引的名字
        unique ix_email (name)
    )
    
  • 联合唯一索引

    create table 表名(
    	id int not null auto_increment,
        name varchar(32) not null,
        unique ix_id_name (id,name)
    )
    
  • 创建表后创建唯一索引

    create unique index 索引名 on 表名(列名);
    
  • 删除唯一索引

    drop unique index 索引名 on 表名;
    

1.2.3 索引和联合索引

  • 普通索引

    create table 表名(
    	id int not null auto_increment,
        name varchar(32) not null,
        index ix_name (name)
    )
    
  • 联合普通索引

    create table 表名(
    	id int not null auto_increment,
        name varchar(32) not null,
        email varchar(64) not null,
        index ix_id_email (id,email)
    )
    
  • 创建表后创建普通索引

    create index 索引名 on 表名(列名);
    
  • 删除普通索引

    drop index 索引名 on 表名;
    

实际开发,根据业务需求来决定是否创建相应的索引

1.3操作表

创建索引后,查询时一定要命中索引

创建索引的优缺点

  • 优点:查询速度快,约束(唯一,主键,联合唯一)
  • 缺点:插入,删除,更新速度比较慢,因为每次操作都需要调整整个B+Tree的数据结构关系

我们会在经常被搜索的列创建索引,从而提高程序的速度

CREATE TABLE `big` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(32) DEFAULT NULL,
    `email` varchar(64) DEFAULT NULL,
    `password` varchar(64) DEFAULT NULL,
    `age` int(11) DEFAULT NULL,
    PRIMARY KEY (`id`),                       -- 主键索引
    UNIQUE KEY `big_unique_email` (`email`),  -- 唯一索引
    index `ix_name_pwd` (`name`,`password`)     -- 联合索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8

一般情况下,通过索引列去搜索都可以命中索引(通过索引结构加速查找)

-- 示例
select * from big where id=5;
select * from big where id>5;
select * from big where email='[email protected]';
select * from big where name='wxy' and password='123';
...

无法命中索引的情况

  • 类型不一致

    -- name是varchar类型不是int
    select * from big where name=123;  -- 未命中
    
    -- 特殊的主键:
    	select * from big where id="123"; -- 命中
    
  • 使用不等于

    -- 找数据中不是wxy,要逐一对比查找
    select * from big where name !='wxy'; -- 未命中
    
    -- 特殊的主键:
    select * from where id!=1; -- 命中
    
  • or,当or条件中有未建立索引的列才会失效

    -- age 未创建索引
    select * from big where id=1 or age=18; -- 未命中
    
    -- 特殊的:
    -- 后面又加了and
    select * from where id=1 or age=18 and name='wxy'-- 命中
    
  • 排序,当根据索引排序时,选择的映射不是索引,不走索引

    -- 映射是所有的数据
    select * from big order by name desc; -- 未命中
    
    select name from big order by name desc; -- 命中
    
    -- 特殊的主键
    select * from big order by id desc; -- 命中
    
  • like,模糊匹配

    select * from big where name like "%xy"; -- 未命中
    select * from big where name like "_xy"; -- 未命中
    select * from big where name like "w_y"; -- 未命中
    
    -- 特别的【通配符在最后面才命中】:
    select * from big where name like 'wx%'; -- 命中
    select * from big where name like 'wx_'; -- 命中
    
  • 使用函数

    -- 要将每一个name翻转
    select * from big where reverse(name)='wxy';  -- 未命中
    
    -- 特别的
    -- 直接将要对比的值翻转,在查找
    select * from big where name=reverse('wxy'); -- 命中
    
  • 最左前缀,如果使用联合索引,要遵循最左前缀原则

    联合索引(name,password)
    name and password  -- 命中
    name			   -- 命中
    password		   -- 未命中
    name or password   -- 未命中
    

1.4执行计划

能够预判sql的执行(只能给一定的参考,不一定完全准确)

explain sql语句;

-- 示例
explain select * from big;

mysql索引和函数及存储过程_第7张图片

比较重要的是type,他是sql性能比较重要的标志,性能排序为:

all

  • ALL,全表扫描,数据表从头到尾找一遍(一般未命中索引,都会执行全表扫描)

    select * from big;
    -- 特别,如果有limit,找到之后就不找了
    select * from big limit 1;
    
  • INDEX,全索引扫描,对索引从头到尾找一遍

    -- name创建了索引
    explain select name from big;
    
  • RANGE,对索引列进行范围查找

    explain select * from big where id>10;
    explain select * from big where id between 2 and 4;
    explain select * from big where name>'wxy';
    
  • INDEX_MERGE,合并索引,使用多个单列索引搜索

    explain select * from big where id=1 or name='wxy';
    
  • REF,根据索引直接去查找(非键)

    select * from big where name='wxy';
    
  • EQ_REF,连表示时操作常见

    explain select big,name,user.id from big left join users on big.age=user.id;
    
  • CONST,常量,表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,CONST表很快

    -- 只能匹配到一行
    explain select * from big where id=11;
    explain select * from big where email='[email protected]';
    
  • SYSTEM,系统,表仅有一行(=系统表),这是CONST联接类型的一个特例

    -- 表中只有一行数据
    explain select * from(select * from big where id=1 limit 1) as A;
    

其他列

id
-- 查询顺序标识

select_type
-- 查询类型
    SIMPLE          简单查询
    PRIMARY         最外层查询
    SUBQUERY        映射为子查询
    DERIVED         子查询
    UNION           联合
    UNION RESULT    使用联合的结果
    ...
    
table
-- 正在访问的表名

partitions
-- 涉及的分区(MySQL支持将数据划分到不同的idb文件中,详单与数据的拆分)。 一个特别大的文件拆分成多个小文件(分区)。

possible_keys
-- 查询涉及到的字段上若存在索引,则该索引将被列出,即:可能使用的索引。

key
-- 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。
-- 例如:有索引但未命中,则possible_keys显示、key则显示NULL。

key_len
-- 表示索引字段的最大可能长度。(类型字节长度 + 变长2 + 可空1)
-- 例如:key_len=195,类型varchar(64),195=64*3+2+1

ref
-- 连表时显示的关联信息。
-- 例如:A和B连表,显示连表的字段信息。

rows
-- 估计读取的数据行数(只是预估值)

filtered
-- 返回结果的行占需要读到的行的百分比。
	explain select * from big where id=1;  -- 100,只读了一个1行,返回结果也是1行。
	explain select * from big where password="27d8ba90-edd0-4a2f-9aaf-99c9d607c3b3";  -- 10,读取了10行,返回了1行。
-- 注意:密码27d8ba90-edd0-4a2f-9aaf-99c9d607c3b3在第10行
	
extra
-- 该列包含MySQL解决查询的详细信息。Using index”
    此值表示mysql将使用覆盖索引,以避免访问表。不要把覆盖索引和index访问类型弄混了。
“Using where”
    这意味着mysql服务器将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不同的索引。
“Using temporary”
    这意味着mysql在对查询结果排序时会使用一个临时表。
“Using filesort”
    这意味着mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。
“Range checked for each record(index map: N)”
    这个意味着没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的。

2.函数

2.1内置函数

count(id)   -- 求个数
max()       -- 求最大值
min()		-- 求最小值
avg()		-- 求平均数
reverse()   -- 翻转
concat(,)   -- 字符串拼接
NOW()       -- 获取当前时间
DATTE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%s')  时间格式化
sleep(1)    -- 睡眠
CHAR_LENGTH(str) 
-- 返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
-- 对于一个包含五个二字节字符集, LENGTH()返回值为 10, 而CHAR_LENGTH()的返回值为5。

CONCAT(str1,str2,...)
-- 字符串拼接
-- 如有任何一个参数为NULL ,则返回值为 NULL。

CONCAT_WS(separator,str1,str2,...)
-- 字符串拼接(自定义连接符)
-- CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。

CONV(N,from_base,to_base)
-- 进制转换
例如:
    SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示

FORMAT(X,D)
-- 将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位,并将结果以字符串的形式返回。若  D 为 0, 则返回结果不带有小数点,或不含小数部分。
例如:
    SELECT FORMAT(12332.1,4); 结果为: '12,332.1000'
    
INSERT(str,pos,len,newstr)
    在str的指定位置插入字符串
        pos:要替换位置其实位置
        len:替换的长度
        newstr:新字符串
    特别的:
        如果pos超过原字符串长度,则返回原字符串
        如果len超过原字符串长度,则由新字符串完全替换
        
INSTR(str,substr)
-- 返回字符串 str 中子字符串的第一个出现位置。

LEFT(str,len)
-- 返回字符串str 从开始的len位置的子序列字符。

LOWER(str)
-- 变小写

UPPER(str)
-- 变大写

LTRIM(str)
-- 返回字符串 str ,其引导空格字符被删除。

RTRIM(str)
-- 返回字符串 str ,结尾空格字符被删去。

SUBSTRING(str,pos,len)
-- 获取字符串子序列

LOCATE(substr,str,pos)
-- 获取子序列索引位置

REPEAT(str,count)
-- 返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。
    若 count <= 0,则返回一个空字符串。
    若str 或 count 为 NULL,则返回 NULLREPLACE(str,from_str,to_str)
-- 返回字符串str 以及所有被字符串to_str替代的字符串from_str 。

REVERSE(str)
-- 返回字符串 str ,顺序和字符顺序相反。

RIGHT(str,len)
-- 从字符串str 开始,返回从后边开始len个字符组成的子序列

SPACE(N)
-- 返回一个由N空格组成的字符串。

SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
    不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。

    mysql> SELECT SUBSTRING('Quadratically',5);
        -> 'ratically'

    mysql> SELECT SUBSTRING('foobarbar' FROM 4);
        -> 'barbar'

    mysql> SELECT SUBSTRING('Quadratically',5,6);
        -> 'ratica'

    mysql> SELECT SUBSTRING('Sakila', -3);
        -> 'ila'

    mysql> SELECT SUBSTRING('Sakila', -5, 3);
        -> 'aki'

    mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
        -> 'ki'

TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str)
    返回字符串 str , 其中所有remstr 前缀和/或后缀都已被删除。若分类符BOTH、LEADIN或TRAILING中没有一个是给定的,则假设为BOTH 。 remstr 为可选项,在未指定情况下,可删除空格。

    mysql> SELECT TRIM('  bar   ');
            -> 'bar'

    mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');
            -> 'barxxx'

    mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');
            -> 'bar'

    mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');
            -> 'barx'   

2.2自定义函数

  • 创建函数

    delimiter $$
    -- 创建函数
    create function f1(
    -- 声明2个参数
        i1 int,
        i2 int)
    returns int
    BEGIN
        declare num int;
        declare maxId int;
        select max(sid) from score into maxId;
        set num = i1 + i2 + maxId;
        return(num);
    END $$
    delimiter ;
    
    mysql> delimiter $$
    mysql> create function f1(
        ->     i1 int,
        ->     i2 int)
        -> returns int
        -> BEGIN
        ->     declare num int;
        ->     declare maxId int;
        ->     select max(sid) from score into maxId;
        ->     set num = i1 + i2 + maxId;
        ->     return(num);
        -> END $$
    Query OK, 0 rows affected (0.02 sec)
    
    mysql> delimiter ;
    
  • 执行函数

    select f1(11,22);
    select f1(11,sid),num from score;
    
    mysql> select f1(11,22);
    +-----------+
    | f1(11,22) |
    +-----------+
    |        85 |
    +-----------+
    1 row in set (0.03 sec)
    
    mysql> select f1(11,sid),num from score;
    +------------+-----+
    | f1(11,sid) | num |
    +------------+-----+
    |         64 |  10 |
    .....
    |        115 |  87 |
    +------------+-----+
    47 rows in set (0.01 sec)
    
  • 删除函数

    drop function f1;
    
    mysql> drop function f1;
    Query OK, 0 rows affected (0.02 sec)
    

3.存储过程

存储过程,是一个存储在mysql中的sql语句集合,当主动去调用存储过程时,其中内部的sql语句1会按照逻辑执行
mysql索引和函数及存储过程_第8张图片

  • 创建存储过程

    delimiter $$
    create procedure p1()
    BEGIN
    	select * from class;
    end $$
    delimiter ;
    
    
    mysql> delimiter $$
    mysql> create procedure p1()
        -> BEGIN
        -> select * from student;
        -> end $$
    Query OK, 0 rows affected (0.02 sec)
    
    mysql> delimiter ;
    
  • 执行存储过程

     call p1();
    
    mysql> call p1();
    +-----+--------+----------+--------+
    | sid | gender | class_id | sname  |
    +-----+--------+----------+--------+
    |   1 ||        1 | 理解   |
    |   2 ||        1 | 钢蛋   |
    |   3 ||        1 | 张三   |
    |   4 ||        1 | 张一   |
    |   5 ||        1 | 张二   |
    |   6 ||        1 | 张四   |
    |   7 ||        2 | 铁锤   |
    |   8 ||        2 | 李三   |
    |   9 ||        2 | 李一   |
    |  10 ||        2 | 李二   |
    |  11 ||        2 | 李四   |
    |  12 ||        3 | 如花   |
    |  13 ||        3 | 刘三   |
    |  14 ||        3 | 刘一   |
    |  15 ||        3 | 刘二   |
    |  16 ||        3 | 刘四   |
    +-----+--------+----------+--------+
    16 rows in set (0.02 sec)
    
    Query OK, 0 rows affected (0.10 sec)
    

    python执行

    import pymysql
    conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',password='wxy575254',db='day27db',charset='utf8')
    cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.callproc('p1')
    result=cursor.fetchall()
    cursor.close()
    conn.close()
    print(result)
    #[{'cid': 1, 'caption': '三年二班'}, {'cid': 2, 'caption': '三年三班'}, {'cid': 3, 'caption': '一年二班'}, {'cid': 4, 'caption': '二年九班'}]
    
  • 删除存储过程

    drop procedure p1;
    
    mysql> drop procedure p1;
    Query OK, 0 rows affected (0.02 sec)
    

3.1参数类型

存储过程的参数可以有如下三种:

  • in,仅用于传入参数用
  • out,仅用于返回值用
  • inout,即可以传入又可以当返回值
-- 创建
delimiter $$
create procedure p2(
  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 $$

mysql> delimiter $$
mysql> create procedure p2(
    -> 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 $$
Query OK, 0 rows affected (0.02 sec)

mysql> delimiter ;

-- 执行
set @t1=4;
set @t2=0;
call p2(1,2,@t1,@t2);
select @t1,@t2;

mysql> set @t1=4;
Query OK, 0 rows affected (0.00 sec)

mysql> set @t2=0;
Query OK, 0 rows affected (0.00 sec)

mysql> call p2(1,2,@t1,@t2);
Query OK, 0 rows affected (0.00 sec)

mysql> select @t1,@t2;
+------+------+
| @t1  | @t2  |
+------+------+
|  104 |    4 |
+------+------+
1 row in set (0.00 sec)

python执行

import pymysql
conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',password='',db='day27db',charset='utf8')
cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
#执行存储过程
cursor.callproc('p2',args=(1,22,3,4))
#获取执行完存储的参数
cursor.execute('select @_p2_0,@_p2_1,@_p2_3,@_p2_3')
result=cursor.fetchall()
cursor.close()
conn.close()
print(result)
#[{'@_p2_0': 1, '@_p2_1': 22, '@_p2_3': 24, '.@_p2_3': 24}]

3.2返回值&结果集

-- 创建
delimiter $$
create procedure p3(
	in n1 int,
    inout n2 int,
     out n3 int
)
begin
	set n2=n1+100;
    set n3=n2+n1+100;
    select * from student;
end $$
delimiter ;

mysql> delimiter $$
mysql> create procedure p3(
    -> in n1 int,
    -> inout n2 int,
    -> out n3 int
    -> )
    -> begin
    -> set n2=n1+100;
    -> set n3=n2+n1+100;
    -> select * from student;
    -> end $$
Query OK, 0 rows affected (0.01 sec)

mysql> delimiter ;
-- 执行
set @t1=4;
set @t2=0;
call p3(1,@t1,@t2);
select @t1,@t2;  -- 获取返回值

mysql> set @t1=4;
Query OK, 0 rows affected (0.00 sec)

mysql> set @t2=0;
Query OK, 0 rows affected (0.00 sec)

mysql> call p3(1,@t1,@t2);
+-----+--------+----------+--------+
| sid | gender | class_id | sname  |
+-----+--------+----------+--------+
|   1 ||        1 | 理解   |
|   2 ||        1 | 钢蛋   |
|   3 ||        1 | 张三   |
|   4 ||        1 | 张一   |
|   5 ||        1 | 张二   |
|   6 ||        1 | 张四   |
|   7 ||        2 | 铁锤   |
|   8 ||        2 | 李三   |
|   9 ||        2 | 李一   |
|  10 ||        2 | 李二   |
|  11 ||        2 | 李四   |
|  12 ||        3 | 如花   |
|  13 ||        3 | 刘三   |
|  14 ||        3 | 刘一   |
|  15 ||        3 | 刘二   |
|  16 ||        3 | 刘四   |
+-----+--------+----------+--------+
16 rows in set (0.00 sec)

Query OK, 0 rows affected (0.05 sec)

mysql> select @t1,@t2;
+------+------+
| @t1  | @t2  |
+------+------+
|  101 |  202 |
+------+------+
1 row in set (0.00 sec)

python执行

import pymysql
conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',password='',db='day27db',charset='utf8')
cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
#执行存储过程
cursor.callproc('p3',args=(22,3,4))
#得到执行存储中的结果集
table=cursor.fetchall()
#获取执行完存储的参数
cursor.execute('select @_p3_0,@_p3_1,@_p3_2')
result=cursor.fetchall()
cursor.close()
conn.close()
print(result)
#[{'@_p3_0': 22, '@_p3_1': 122, '@_p3_2': 244}]

print(table)
#[{'sid': 1, 'gender': '男', 'class_id': 1, 'sname': '理解'}, {'sid': 2, 'gender': '女', 'class_id': 1, 'sname': '钢蛋'}, {'sid': 3, 'gender': '男', 'class_id': 1, 'sname': '张三'}, {'sid': 4, 'gender': '男', 'class_id': 1, 'sname': '张一'}, {'sid': 5, 'gender': '女', 'class_id': 1, 'sname': '张二'}, {'sid': 6, 'gender': '男', 'class_id': 1, 'sname': '张四'}, {'sid': 7, 'gender': '女', 'class_id': 2, 'sname': '铁锤'}, {'sid': 8, 'gender': '男', 'class_id': 2, 'sname': '李三'}, {'sid': 9, 'gender': '男', 'class_id': 2, 'sname': '李一'}, {'sid': 10, 'gender': '女', 'class_id': 2, 'sname': '李二'}, {'sid': 11, 'gender': '男', 'class_id': 2, 'sname': '李四'}, {'sid': 12, 'gender': '女', 'class_id': 3, 'sname': '如花'}, {'sid': 13, 'gender': '男', 'class_id': 3, 'sname': '刘三'}, {'sid': 14, 'gender': '男', 'class_id': 3, 'sname': '刘一'}, {'sid': 15, 'gender': '女', 'class_id': 3, 'sname': '刘二'}, {'sid': 16, 'gender': '男', 'class_id': 3, 'sname': '刘四'}]

3.3事务&异常

事务,成功都成功,失败都失败

delimiter $$
create procedure p4(
	out p_return_code tinyint
)
begin
	declare exit handler for sqlexception
	begin
		set p_return_code=1;
		rollback;
	end;
	declare exit handler for sqlwarning
	begin
		set p_return_code=2;
		rollback;
	end;
	start transaction;
		delete from student;
		insert into class(caption)values('all');
	commit;
	set p_return_code=0;
end $$
delimiter ;
mysql> delimiter $$
mysql> create procedure p4(
    -> out p_return_code tinyint
    -> )
    -> begin
    -> declare exit handler for sqlexception
    -> begin
    -> set p_return_code=1;
    -> rollback;
    -> end;
    -> declare exit handler for sqlwarning
    -> begin
    -> set p_return_code=2;
    -> rollback;
    -> end;
    -> start transaction;
    -> delete from student;
    -> insert into class(caption)values('all');
    -> commit;
    -> set p_return_code=0;
    -> end $$
Query OK, 0 rows affected (0.02 sec)

mysql> delimiter ;
 set @ret=100;
 call p4(@ret);
 select @ret;

mysql> set @ret=100;
Query OK, 0 rows affected (0.00 sec)

mysql> call p4(@ret);
Query OK, 0 rows affected (0.01 sec)

mysql> select @ret;
+------+
| @ret |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

python执行

import pymysql
conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',password='',db='day27db',charset='utf8')
cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
#执行存储过程
cursor.callproc('p4',args=(100))
#获取执行完存储的参数
cursor.execute('select @_p4_0')
result=cursor.fetchall()
cursor.close()
conn.close()
print(result)

3.4游标

delimiter $$
create procedure p5()
begin
	declare cid int;
	declare caption varchar(50);
	declare done int default false;
	declare my_cursor cursor for select cid,caption from class;
	declare continue handler for not found set done =TRUE;
	open my_cursor;
		xxoo:loop
			fetch my_cursor into cid,caption;
			if done then 
				leave xxoo;
			end if;
			insert into teacher(tname) values(caption);
		end loop xxoo;
	close my_cursor;
end $$
delimiter ;


mysql> delimiter $$
mysql> create procedure p5()
    -> begin
    -> declare cid int;
    -> declare caption varchar(50);
    -> declare done int default false;
    -> declare my_cursor cursor for select cid,caption from class;
    -> declare continue handler for not found set done =TRUE;
    -> open my_cursor;
    -> xxoo:loop
    -> fetch my_cursor into cid,caption;
    -> if done then
    -> leave xxoo;
    -> end if;
    -> insert into teacher(tname) values(caption);
    -> end loop xxoo;
    -> close my_cursor;
    -> end $$
Query OK, 0 rows affected (0.01 sec)

-- done为false,没有值,插不进去,报错
call p5();

mysql> call p5();
ERROR 1048 (23000): Column 'tname' cannot be null

4视图

视图其实是一个虚拟表(非真实存在),本质是(根据sql语句获取动态的数据集,并为其命名),用户使用时只需要使用【名称】即可获取数据集

select
	*
from 
	(select nid,name from tb1 where nid>2) as A
where 
	A.name>'wxy';
  • 创建视图

    create view A as select nid,name from tb1 where nid>2;
    
  • 使用视图

    SELECT * from A;
    
  • 删除视图

    drop view A;
    
  • 修改视图

    alter view A as sql语句;
    

注意,基于视图只能查询,不能执行增加,修改,删除,如果源表发生变化,试图也会发生变化

5.触发器

mysql索引和函数及存储过程_第9张图片

对于某个表进行【删除/增加/修改】操作的前后如果希望触发某个特定的行为时,可使用触发器

创建触发器

  • 插入前

    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 after 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 after update on tb1 for each row
    begin
    ...
    end
    
  • 更新后

    create trigger tri_after_update_tb1 after update on tb1 for each row
    begin
    ...
    end
    

删除触发器

drop trigger tri_after_insert_tb1;

示例

  • 在t1表中插入数据之前,先在t2表中插入一行数据
delimiter $$
create trigger tri_before_insert_tb1 before insert on tb1 for each row
begin
	-- 插入的是new,新数据
	-- 如果插入的name='wxy',就在t2表中插入
	if new.name='wxy' then
		insert into t2 (name) values(new.id)
	end if;
end $$
delimiter ;
  • 在t1表中删除数据之后,先在t2表中插入一行数据
delimiter $$
create trigger tri_after_delete_tb1 after delete on tb1 for each row
begin
	-- 删除的是old,旧数据
	-- 如果删除的name='wxy',就在t2表中插入
	if old.name='wxy' then
		insert into t2 (name) values(old.id)
	end if;
end
delimiter ;

注意;NEW表示新数据,OLD表示旧数据

你可能感兴趣的:(sql数据库,sql,数据库)