week9:mysql视图、存储、触发器、事务、函数、索引

一、视图

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

1、创建视图

--格式:create view 视图名称 as  SQL语句
create view v1 as select nid, name from A Where nid > 4

2、删除视图

--格式:drop view 视图名称

drop view v1

3、修改视图

-- 格式:alter view 视图名称 as SQL语句

alter view  v1 as select 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

4、使用视图

使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。

二、存储过程

存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。

1、创建存储过程

-- 创建存储过程

delimiter $$   ##修改终止符
create procedure p1()
BEGIN
    select * from t1;
END $$
delimiter ;


-- 执行存储过程    mysql终端

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 ;

-- 执行存储过程   mysql终端
set @t1 =4;
set @t2 = 0;
CALL p1 (1, 2 ,@t1, @t2);
SELECT @t1,@t2;

注意:不管是视图还是存储过程,都在服务端。 

2、删除存储过程

drop procedure proc_name;

3、执行存储过程

import pymysql

conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123', db='tb1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 执行存储过程
cursor.callproc('p1')

result = cursor.fetchall()


cursor.close()
conn.close()
print(result)

有参数的存储过程的执行

import pymysql

conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123', db='tb1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 执行存储过程,获取存储过程的结果集,将返回值设置给了@_存储过程名_序号
r1=cursor.callproc('p1', args=(1, 22, 3, 4))
#set @_p1_0 = 1  set @_p1_1 = 22  set @_p1_2 = 3  set @_p1_3 = 4
#call p1(1,22,3,4)
print(r1)
result1 = cursor.fetchall()
print(result1)
# 获取执行完存储的参数
r2=cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
print(r2)
result2 = cursor.fetchall()
print(result2)

cursor.close()
conn.close()


三、其他

1、条件语句

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 ;

2、循环语句

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 ;
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 ;
begin
    
    declare i int default 0;
    loop_label: loop
        
        set i=i+1;
        if i<8 then
            iterate loop_label;
        end if;
        if i>=10 then
            leave loop_label;
        end if;
        select i;
    end loop loop_label;

end

3、动态执行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;  ##必须写@p1
    deallocate prepare prod; 

end\\
delimiter ;

四、触发器

1、创建触发器

# 插入前
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
delimiter //
create trigger tri_before_insert_tb1 before insert on tb1 for each row

begin

IF NEW. NAME == 'alex' 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 ;

 特别的:NEW表示即将插入的数据行,OLD表示即将删除的数据行。

2、删除触发器 

DROP TRIGGER tri_after_insert_tb1;

3、使用触发器

触发器无法由用户直接调用,而知由于对表的【增/删/改】操作被动引发的。

insert into tb1(num) values(666)

五、事务

事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。

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 ;

调用:

set @i =0;

call p1(@i);

select @i;

六、函数

1、MySQL中提供了许多内置函数,例如:

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位置的子序列字符。
right(str,len)
        返回字符串str 从右开始的len位置的子序列字符。
substring(str,pos,len)
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'

LOWER(str)
        变小写

UPPER(str)
        变大写
ltrim(str)
        返回字符串 str ,其引导空格字符被删除。
rtrim(str)
        返回字符串 str ,结尾空格字符被删去。
trim({both | leading | trailing} remstr from str)   trim(remstr from] str)
返回字符串 str , 其中所有remstr 前缀和/或后缀都已被删除。
若分类符both、leading或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'
locate(substr,str,pos)
        获取子序列索引位置  pos 代表起始位置
repeat(str,count)
        返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。
        若 count <= 0,则返回一个空字符串。
        若str 或 count 为 NULL,则返回 NULL 。
reverse(str)
        返回字符串 str ,顺序和字符顺序相反。
space(N)
        返回一个由N空格组成的字符串。
select count(1) from tb1

 2、自定义函数

delimiter \\
create function f1(i1 int,i2 int)
returns int
begin
    declare num int;
    set num = i1 + i2;
    return(num);
end \\
delimiter ;

在终端中调用

# 在查询中使用
select f1(11,nid) ,name from tb2;

 3、删除函数

drop function func_name;

4、执行函数

# 获取返回值
declare @i VARCHAR(32);
select UPPER('alex') into @i;
SELECT @i;

函数:
            SQL不允许
            declare a int;
            # set a = 123;  ##赋值
            select nid into a from student where name='alex'   # nid: 11   name:alex   把nid赋值给a
            # a = 11

            return 返回
            # select 函数名(参数)

存储过程:
            sql语句
            intout,out构造返回值
            call: 存储过程名称

七、索引

索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构。类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可。具有加速查找的功能。

为什么索引这么快?

MySQL中常见索引:

1、普通索引(约束列数据可以重复)

普通索引仅有一个功能:加速查询

创建表+索引

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)  ##ix_name 为名字,为name创建索引
)

创建索引 

create index ix_name on 表名(column_name)

 删除索引

drop ix_name on 表名;

 查看索引

show index from 表名;

注意:对于创建索引时如果是BLOB 和 TEXT 类型,必须指定length。

create index ix_extra on in1(extra(32));

2、唯一索引(约束列数据不能重复,null)

唯一索引有两个功能:加速查询 和 唯一约束(可含null)

创建表+唯一索引

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 表名

 3、主键索引(约束列数据不能重复,不能null)

主键有两个功能:加速查询 和 唯一约束(不可含null)

创建表+创建主键

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;

4、组合索引(多列可以创建一个索引文件)

组合索引是将n个列组合成一个索引

其应用场景为:频繁的同时使用n列来进行查询,如:where n1 = 'alex' and n2 = 666。

创建表

create table in3(
    nid int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    extra text
)

创建组合索引

create index ix_name_email on in3(name,email);

如上创建组合索引之后,查询:

  • name and email  -- 使用索引
  • name                 -- 使用索引
  • email                 -- 不使用索引

注意:对于同时搜索n个条件时,组合索引的性能好于多个单一索引合并。

5、覆盖索引

select * from tb where nid=1
# 先去索引中找,
# 在去数据表找
        
select nid from tb where nid < 10 
# 先去索引中找
            
 -- 情况应用上索引,并且不用去数据表中操作,覆盖索引0
 -- 只需要在索引表中就能获取到数据时,

6、合并索引

nid    name(单独索引)    email(单独索引)    pwd
        
select * from tb where name='alex'
select * from tb where email='[email protected]'      
select * from tb where name='alex' or email='[email protected]'



nid   name(组)    email(合)    pwd
# 最左前缀
        
select * from tb where name='alex'
select * from tb where email='[email protected]' ########无法满足########       
select * from tb where name='alex' or email='[email protected]'
        
       
用户表:
nid   username(组)    password(合)
1           alex                     123
2         shaogbing             123
             
            
select * from tb where username='xx' and password='xx'
select * from tb where username='xx'
 # select * from tb where password='xx'
            
--> 组合和合并索引取舍?业务需求来决定

7、执行计划 - 相对比较准确表达出当前SQL运行状况

查询时的访问方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const

是否走索引,不走索引  explain SQL语句
                
1、explain SQL语句
type: ALL    - 全数据表扫描
type: index  - 全索引表扫描
            
2、limit 
select * from tb1 where email='123'
            
select * from tb1 where email='123' limit 1;
            
 -----SQL: ALL、Index,都是有优化的余地 -------

range 

对索引列进行范围查找
                            select *  from tb1 where name < 'alex';
                            PS:
                                between and
                                in
                                >   >=  <   <=  操作
                                注意:!= 和 > 符号
INDEX_MERGE     合并索引,使用多个单列索引搜索
                select *  from tb1 where name = 'alex' or nid in (11,22,33);
EQ_REF          连接时使用primary key 或 unique类型
                select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid;
CONST           常量
                表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次。
                select nid from tb1 where nid = 2 ;

SYSTEM          系统
                表仅有一行(=系统表)。这是const联接类型的一个特例。
                select * from (select nid from tb1 where nid = 1) as A;
possible_keys
        可能使用的索引

    key
        真实使用的

    key_len
        MySQL中使用索引字节长度

    rows
        mysql估计为了找到所需的行而要读取的行数 ------ 只是预估值
    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列中索引的位图,并且是冗余的。

8、如何命中索引?

如果以错误的方式使用,则即使建立索引也会不奏效。
即使建立索引,索引也不会生效:

- like '%xx'
    select * from tb1 where name like '%cn';##%在前面不可以。%在后面走索引
- 使用函数
    select * from tb1 where reverse(name) = 'wupeiqi';  ##不能走索引
- or
    select * from tb1 where nid = 1 or email = '[email protected]';  ##不能走索引
    特别的:当or条件中有未建立索引的列才失效,以下会走索引
            select * from tb1 where nid = 1 or name = 'seven';
            select * from tb1 where nid = 1 or email = '[email protected]' and name = 'alex'
- 类型不一致
    如果列是字符串类型,传入条件是必须用引号引起来,不然不能走索引
    select * from tb1 where name = 999;
- !=
    select * from tb1 where name != 'alex'
    特别的:如果是主键,则还是会走索引
        select * from tb1 where nid != 123
- >
    select * from tb1 where name > 'alex'
    特别的:如果是主键或索引是整数类型,则还是会走索引
        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)

- 连表时注意条件类型需一致

- 索引散列值(重复少)不适合建索引,例:性别不适合

9、分页

limit x,m :  表x+m
    
where nid>10000 直接跳过 前10000条数,继续往下扫 

每页显示10条:
当前 118 120, 125

倒序:
            大      小
            980    970  7 6  6 5  54  43  32

21 19 98 下一页:
select * from tb1 where nid < (select nid from (select nid from tb1 where nid < 当前页最小值 order by nid desc limit 每页数据 *【页码-当前页】) A order by A.nid asc limit 1)  order by nid desc limit 10;



select * from tb1 where nid < (select nid from (select nid from tb1 where nid < 970  order by nid desc limit 40) A order by A.nid asc limit 1)  order by nid desc limit 10;


上一页:
select * from tb1 where nid < (select nid from (select nid from tb1 where nid > 当前页最大值 order by nid asc limit 每页数据 *【当前页-页码】) A order by A.nid asc limit 1) order by nid desc limit 10;


select * from tb1 where nid < (select nid from (select nid from tb1 where nid > 980 order by nid asc limit 20) A order by A.nid desc limit 1)  order by nid desc limit 10;


    

猜你喜欢

转载自blog.csdn.net/MaHilda/article/details/82773771
今日推荐