视图 触发器 函数 流程控制 存储过程 SQL注入问题

9 篇文章 0 订阅

视图
视图就是通过查询得到一张虚拟表(非真实存在),然后保存下来,下次可以直接使用,用户使用时直接使用【视图表名】即可获取结果集。
视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用。
 

使用视图的注意点:

  1.创建视图在硬盘上只会有表结构,没有表数据,数据并没有存在硬盘(数据还是来自之前的表)

  2.视图一般只用来查询,里面的数据不要进行修改,可能会影响真正的表

创建视图语法

  create view 表名 as 虚拟表的查询语句
 

# 创建一张数据表
select * from employee;
+----+--------+--------+-----+------------+--------------+--------------+----------+--------+-----------+
| id | name   | sex    | age | hire_date  | post         | post_comment | salary   | office | depart_id |
+----+--------+--------+-----+------------+--------------+--------------+----------+--------+-----------+
|  1 | sun    | male   |  18 | 2017-03-01 | 猥琐欲为     | NULL         |  1000.22 |    401 |         1 |
|  2 | haha   | male   |  78 | 2015-03-02 | teacher      | NULL         | 10000.31 |    401 |         1 |
|  3 | xixi   | male   |  81 | 2013-03-05 | teacher      | NULL         |  8300.31 |    401 |         1 |
|  4 | dudu   | male   |  72 | 2015-03-12 | teacher      | NULL         |  3100.00 |    401 |         1 |
|  5 | biubiu | male   |  28 | 2014-03-18 | teacher      | NULL         |  5000.00 |    401 |         1 |
|  6 | lala   | female |  58 | 2021-03-23 | teacher      | NULL         |  1020.00 |    401 |         1 |
|  7 | bubu   | male   |  38 | 2015-05-20 | teacher      | NULL         |  1501.00 |    401 |         1 |
|  8 | bobo   | male   |  55 | 2011-03-10 | teacher      | NULL         |  8222.00 |    401 |         1 |
|  9 | 哈哈   | male   |  48 | 2012-03-21 | sale         | NULL         |  3333.00 |    402 |         2 |
| 10 | 呵呵   | male   |  55 | 2013-06-20 | sale         | NULL         |  1050.00 |    402 |         2 |
| 11 | 西西   | male   |  38 | 2014-07-20 | sale         | NULL         |  1200.31 |    402 |         2 |
| 12 | 嘟嘟   | male   |  18 | 2018-12-01 | sale         | NULL         | 10011.00 |    402 |         2 |
| 13 | 萌萌   | male   |  38 | 2010-05-02 | sale         | NULL         | 10020.00 |    402 |         2 |
| 14 | 拉拉   | male   |  58 | 2005-03-25 | sale         | NULL         |  6250.00 |    402 |         2 |
| 15 | 娜娜   | male   |  18 | 2010-03-19 | operation    | NULL         | 20000.00 |    403 |         3 |
| 16 | 宝宝   | female |  18 | 2015-04-05 | operation    | NULL         | 20000.00 |    403 |         3 |
| 17 | 辣辣   | male   |  18 | 2009-03-15 | operation    | NULL         | 18220.00 |    403 |         3 |
| 18 | 蟹蟹   | male   |  18 | 2008-03-20 | operation    | NULL         | 50000.00 |    403 |         3 |
+----+--------+--------+-----+------------+--------------+--------------+----------+--------+-----------+

# 查看salary大于10000的虚拟表
select name,age,post from employee where salary > 10000;
+--------+-----+-----------+
| name   | age | post      |
+--------+-----+-----------+
| haha   |  78 | teacher   |
| 嘟嘟   |  18 | sale      |
| 萌萌   |  38 | sale      |
| 娜娜   |  18 | operation |
| 宝宝   |  18 | operation |
| 辣辣   |  18 | operation |
| 蟹蟹   |  18 | operation |
+--------+-----+-----------+

# 创建视图,将虚拟表的结果产生的表结构保存了,并没有表数据
# 每次我们使用视图的表数据,都是直接触发as后面的sql语句运行,通过查询得到的表数据
create view new_employee as select name,age,post from employee where salary > 10000;

# 查看视图数据
select * from new_employee;
+--------+-----+-----------+
| name   | age | post      |
+--------+-----+-----------+
| haha   |  78 | teacher   |
| 嘟嘟   |  18 | sale      |
| 萌萌   |  38 | sale      |
| 娜娜   |  18 | operation |
| 宝宝   |  18 | operation |
| 辣辣   |  18 | operation |
| 蟹蟹   |  18 | operation |
+--------+-----+-----------+

# 查看数据库指定目录下的视图数据,只有表结构
ls /service/mysql/data/db01
# new_employee.frm

# 修改视图表(本质就是修改视图as后面的sql语句规则)
alter view new_employee as select name,age from employee where id < 3;

select * from new_employee;
+------+-----+
| name | age |
+------+-----+
| sun  |  18 |
| haha |  78 |
+------+-----+

# 删除视图
drop view new_employee;
 

触发器

使用触发器可以定制用户对表进行【增、删、改】操作时前后的行为,注意:没有查询!!!

使用触发器可以帮助我们实现监控,日志,自动处理异常等等...

触发器可以在6种情况下自动触发 增前增后 删前删后 改前改后

创建触发器基本语法结构

  create trigger 触发器的名字 before/after insert/update/delete 表名
  for each row
  begin 
  	sql语句
  end

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

当cmd表中的记录success字段是no那么就触发触发器去执行errlog表中插入的数据

# 创建表字段
create table cmd(
id int primary key auto_increment,
user char(32),
priv char(10),
cmd char(64),
sub_time datetime,              # 提交时间
success enum("yes","no")        # no代表执行失败
);

create table errlog(
id int primary key auto_increment,
err_cmd char(64),
err_time datetime
);

# 创建触发器
delimiter $$                # delimiter 声明sql语句的结束符号
# create trigger 触发器名 after insert 表名,插入表数据后被动引发触发器执行
create trigger tri_after_insert_cmd after insert on cmd 
for each row        
begin
    if new.success = "no" then      # new 指代的就是cmd表中的一条条数据对象(new.success ==> cmd.success)
    insert into errlog(err_cmd,err_time) values(new.cmd,new.sub_time);        # 必须加分号
    end if;            # 必须加分号
end $$
delimiter ;            # 重新声明sql语句符号为;

# 往表cmd中插入记录,触发触发器,根据if的条件决定是否插入错误日志
insert into cmd(user,priv,cmd,sub_time,success)
values("nana","0755","is -1 /etc",now(),"yes"),
      ("nana","0755","cat /etc/password",now(),"no"),
      ("nana","0755","useradd xxx",now(),"no");

select * from cmd;
+----+------+------+-------------------+---------------------+---------+
| id | user | priv | cmd               | sub_time            | success |
+----+------+------+-------------------+---------------------+---------+
|  1 | nana | 0755 | is -l /etc        | 2021-07-05 22:18:01 | yes     |
|  2 | nana | 0755 | cat /ect/password | 2021-07-05 22:18:01 | no      |
|  3 | nana | 0755 | useradd xxx       | 2021-07-05 22:18:01 | no      |
+----+------+------+-------------------+---------------------+---------+

# 查询错误日志,发现有两条数据
select * from errlog;
+----+-------------------+---------------------+
| id | err_cmd           | err_time            |
+----+-------------------+---------------------+
|  1 | cat /ect/password | 2021-07-05 22:18:01 |
|  2 | useradd xxx       | 2021-07-05 22:18:01 |
+----+-------------------+---------------------+

# 删除触发器
drop trigger tri_after_insert_cmd;  

函数

函数只能在sql语句中使用,不能独立调用!!!
若要想在begin…end…中写sql,请用存储过程!!!

内置函数

- 常见的内置函数
# 查看当前登陆的用户
select user();
+----------------+
| user()         |
+----------------+
| root@localhost |
+----------------+

# 查看当前时间
select now();
+---------------------+
| now()               |
+---------------------+
| 2021-07-05 22:45:03 |
+---------------------+

# 随机生成一个小数
select rand();
+--------------------+
| rand()             |
+--------------------+
| 0.6458554832691843 |
+--------------------+

# 四舍五入取值,保留两位小数
select round(6.666,2);
+----------------+
| round(6.666,2) |
+----------------+
|           6.67 |
+----------------+

- date_format内置函数,日期格式的拼接
# 创建表格
create table blog(
id int primary key auto_increment,
name char(32),
sub_time datetime
);

# 插入数据
insert into blog(name,sub_time) values
('1','2015-03-01 11:31:21'),
('2','2015-03-11 16:31:21'),
('3','2016-07-01 10:21:31'),
('4','2016-07-22 09:23:21'),
('5','2016-07-23 10:11:11'),
('6','2016-07-25 11:21:31'),
('7','2017-03-01 15:33:21'),
('8','2017-03-01 17:32:21'),
('9','2017-03-01 18:31:21');

# 查看表的数据
select * from blog;                                                                  
+----+------+---------------------+
| id | name | sub_time            |
+----+------+---------------------+
|  1 | 1    | 2015-03-01 11:31:21 |
|  2 | 2    | 2015-03-11 16:31:21 |
|  3 | 3    | 2016-07-01 10:21:31 |
|  4 | 4    | 2016-07-22 09:23:21 |
|  5 | 5    | 2016-07-23 10:11:11 |
|  6 | 6    | 2016-07-25 11:21:31 |
|  7 | 7    | 2017-03-01 15:33:21 |
|  8 | 8    | 2017-03-01 17:32:21 |
|  9 | 9    | 2017-03-01 18:31:21 |
+----+------+---------------------+

# 提取sub_time的字段数据,格式按照"年月"来进行分组
select date_format(sub_time,"%Y-%m") "时间(年-月)",count(id) from blog group by date_format(sformat(sub_time,"%Y-%m");
+-----------------+-----------+
| 时间(年-月)     | count(id) |
+-----------------+-----------+
| 2015-03         |         2 |
| 2016-07         |         4 |
| 2017-03         |         3 |
+-----------------+-----------+
 

自定义函数

函数中不要写sql语句(否则会报错),函数仅仅只是一个功能,是一个在sql中被应用的功能

# delimiter 声明sql语句的结束符号
delimiter $$        

# 定义一个函数f1
create function f1(
    a int,
    b int)
    returns int
    begin
    declare num int;
    set num = a+b;
    return(num);
    end$$
    
delimiter ; 

# 调用函数
select f1(2,3);
+---------+
| f1(2,3) |
+---------+
|       5 |
+---------+

# 创建一个表
create table t1(id int);

# 调用函数在表中插入数据
mysql> insert t1 values(f1(2,3));

# 查看表数据
select * from t1;
+------+
| id   |
+------+
|    5 |
+------+

# 删除自定义函数
drop function f1;
 

流程控制

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 ;

# 调用存储过程
call proc_if();
+---+
| 7 |
+---+
| 7 |
+---+

# 删除存储过程
drop procedure proc_if;

while循环

# 定义一个while循环的存储过程
delimiter //
create procedure proc_while ()
begin
    declare num int ;
    set num = 0 ;
    while num < 3 do   # 如果num小于3,do表示进入循环
        select
            num ;
        set num = num + 1 ;
    end while ;

end //
delimiter ;

# 调用存储过程
call proc_while();
+------+
| num  |
+------+
|    0 |
+------+

+------+
| num  |
+------+
|    1 |
+------+

+------+
| num  |
+------+
|    2 |
+------+

# 删除存储过程
drop procedure proc_while;
 

存储过程

存储过程常见的三种开发模型

第一种
应用程序:程序员编写代码开发,调用存储过程
mysql:DBA提前编写好存储过程,供程序员调用

  优点:开发效率提升了,执行效率也提升了 

  缺点:考虑到人为因素,跨部门沟通问题,后续的存储国过程扩展性差

第二种
应用程序:程序员编写开发代码,涉及到数据库操作也是自己编写sql语句

  优点:扩展性很高 

  缺点:开发效率低,编写sql语句太过繁琐,后续还需要考虑sql优化的问题

第三种
应用程序:只写程序代码,不写sql语句,基于别人写好的框架(ORM框架)直接调用sql语句即可

   优点:开发效率比上面两种情况都要高 

   缺点:语句扩展性差,可能会出现效率低下问题

通常情况下,我们都是用第三种开发模型,程序运行出现效率低下的问题,再手动写sql语句。

创建无参存储过程

# 定义存储过程
delimiter //
create procedure p1()
begin
    select * from blog;
    insert into blog(name,sub_time) values("nana",now());
end //
delimiter ;

# 在mysql中调用存储过程
call p1();

# 删除存储过程
drop procedure p1;

# 在python中基于pymysql调用
cursor.callproc('p1') 
print(cursor.fetchall())
 

创建有参存储过程

对于存储过程,可以接收参数,其参数有三类:

  in          	仅用于传入参数用
  out        	仅用于返回值用
  inout     	既可以传参又可以当作返回值

in 仅用于传参

# 定义存储过程
delimiter //
create procedure p2(
    in n1 int,
    in n2 int
)
begin    
    select * from blog where id > n1;
end //
delimiter ;

# 在mysql中调用
call p2(3,2);

# 删除存储过程
drop procedure p2;

# 在python中基于pymysql调用
cursor.callproc('p2',(3,2))
print(cursor.fetchall())
 

out 仅用于返回值用

# 定义存储过程
delimiter //
create procedure p3(
    in n1 int,
    out res int
)
begin
    select * from blog where id > n1;
    set res = 1;
end //
delimiter ;

# 在mysql中调用存储过程
set @res=0;             # 0代表假(执行失败),1代表真(执行成功)
call p3(3,@res);
select @res;

# 删除存储过程
drop procedure p3;

# 在python中基于pymysql调用
cursor.callproc('p3',(3,0))     # 0相当于set @res=0
print(cursor.fetchall())     # 查询select的查询结果

cursor.execute('select @_p3_0,@_p3_1;')     # @p3_0代表第一个参数,@p3_1代表第二个参数,即返回值
print(cursor.fetchall())
 

inout 既可以传参又可以当作返回值

# 定义存储过程
delimiter //
create procedure p4(
    inout n1 int
)
begin
    select * from blog where id > n1;
    set n1 = 1;
end //
delimiter ;

# 在mysql中调用存储过程
set @x=3;
call p4(@x);
select @x;

# 删除存储过程
drop procedure p4;

# 在python中基于pymysql调用
cursor.callproc('p4',(3,))
print(cursor.fetchall())     # 查询select的查询结果

cursor.execute('select @_p4_0;') 
print(cursor.fetchall())
 

SQL注入问题

模拟sql注入问题

sql注入问题,我们可以通过特殊的符号改变sql语句的语法规则

  如下所示,我们编写一个登陆功能,需要连接数据库,校验数据库的表数据,可以使用特殊的符号改变sql语句的语法规则
  所以市场上有一些软件,在用户注册的时候,会限制用户设置密码禁止使用一些特殊字符

1. 创建远程登陆用户,并插入用户数据

# 登陆数据库
mysql -uroot -p123

# 创建mysql用户
grant all on *.* to "nana"@"%" identified by "123";

# 刷新用户表
flush privileges;

# 创建本地用户成功之后使用ip登陆做一次测试
mysql -unana -p123 -h192.168.15.51

# 创建表格
create database db01;
use db01;
create table user(id int,name varchar(16),password varchar(32));

# 插入数据
insert user values(1,"nana","123");
insert user values(2,"haha","456");

# 查看用户表数据
mysql> select * from user;
+------+------+----------+
| id   | name | password |
+------+------+----------+
|    1 | nana | 123      |
|    2 | haha | 456      |
+------+------+----------+
 

2. 模拟sql注入问题

# 使用pymysql模块连接登陆数据库
import pymysql

# 连接数据库
conn = pymysql.connect(
    host="195.168.15.51",
    port=3306,
    user="nana",
    password="123",
    database="db01",
    charset="utf8"
)
# 游标:相当于mysql> ,给mysql提供sql语句
cursor = conn.cursor()

inp_name = input("username>>>:").strip()
inp_passwd = input("password>>>:").strip()
sql = "select * from user where name ='%s' and password = '%s'" % (inp_name, inp_passwd)
print(sql)
# 将命令提交给mysql数据库进行校验
rows = cursor.execute(sql)
if rows:
    print("登陆成功")
else:
    print("用户名密码错误")

cursor.close()  # 关闭游标
conn.close()  # 断开连接

=================================================运行代码===========================================================
# sql语句-- 表示注释
username>>>:kkkk' or True -- '
password>>>:
select * from user where name ='kkkk' or True -- '' and password = '';
登陆成功
 

sql注入问题解决方案

  方法一: 日常生活中很多软件在注册的时候禁止使用特殊符号

  方法二: 使用python的execute内置函数帮忙拼接,execute默认会把特殊符号自动过滤掉,如下所示

import pymysql

# 连接数据库
conn = pymysql.connect(
    host="192.168.15.51",
    port=3306,
    user="nana",
    password="123",
    database="db01",
    charset="utf8"
)
# 游标:相当于mysql> ,给mysql提供sql语句
cursor = conn.cursor()

inp_name = input("username>>>:").strip()
inp_passwd = input("password>>>:").strip()
# 不要手动拼接数,先用%s占位,之后将需要拼接的数据直接交给execute方法即可
sql = "select * from user where name =%s and password = %s"
print(sql)
# 将命令提交给mysql数据库进行校验
# execute:自动识别sql里面的%s然后用后面元组里面的数据替换,并且会帮你把特殊符号自动过滤掉
rows = cursor.execute(sql,(inp_name,inp_passwd))
if rows:
    print("登陆成功")
else:
    print("用户名或密码错误")

cursor.close()  # 关闭游标
conn.close()  # 断开连接

==================================================运行代码==============================================================
username>>>:kkk' or True -- '
password>>>:
select * from user where name =%s and password = %s
用户名或密码错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值