一、SQL注入问题

在使用pymysql进行信息查询时,推荐使用传参的方式,禁止使用字符串拼接方式,因为字符串拼接往往会带来sql注入的问题

 # -*- coding:utf-8 -*-
# author: cdc
# date: 2019/3/18 import pymysql conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='cdc19951216',db='test',charset='utf8') cursor = conn.cursor() # 传参方式查询数据
cursor.execute('select * from user_info where name=%s and password=%s',('cdc','',))
res = cursor.fetchone()
print(res) # 执行结果 >>>> (1, 'cdc', '123456')

传参方式查询

 # -*- coding:utf-8 -*-
# author: cdc
# date: 2019/3/18 import pymysql conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='cdc19951216',db='test',charset='utf8') cursor = conn.cursor() # 字符串拼接方式查询
sql = 'select * from user_info where name="%s" and password="%s"'
inp = ('cdc','')
sql = sql % inp
cursor.execute(sql)
res = cursor.fetchone()
print(res) # 执行结果 >>>>> (1, 'cdc', '123456')

字符串拼接方式查询

乍一看,两种方式都可以执行成功,但是只要对字符串拼接的方法稍微改动一下就能很明显的看出此类方式的弊端

# -*- coding:utf-8 -*-
# author: cdc
# date: 2019/3/18 import pymysql conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='cdc19951216',db='test',charset='utf8') cursor = conn.cursor() sql = 'select * from user_info where name="%s" and password="%s"'
inp = ('cdc" and 1=1 -- ','')
sql = sql % inp
cursor.execute(sql)
res = cursor.fetchone()
print(res) # 执行结果 >>>>> (1, 'cdc', '123456')

按理来说,数据库中并未满足条件的数据,但是还是可以执行成功,这是因为将字符串'cdc" and 1=1 -- '替换掉对应的占位符后会将sql语句变为:

 select * from user_info where name="cdc" and 1=1 -- " and password="%s"

此时后面password的条件已经被注释掉了,真正执行的判断条件是name='cdc'和1=1,这个条件恒为true,因此无论如何都可以从数据库中查询到信息,因此使用字符串拼接的方式进行查询风险很大。

二、视图

视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,并可以将其当作表来使用。(实例化来说,可能某个需求需要重复的对某张表中的某些数据进行反复操作,每次重写重复的sql与是十分没有必要的,可以先将要操作的数据提取出来,作为一张临时的表)

1、创建视图

 --格式:CREATE VIEW 视图名称 AS  SQL语句

 CREATE VIEW temp1 AS SELECT no,name from tb3 where tb3.part_no>2

2、删除视图

 --格式:DROP VIEW 视图名称

 DROP VIEW temp1

3、修改视图

 -- 格式:ALTER VIEW 视图名称 AS SQL语句

 ALTER VIEW temp1 AS SELECT no,name,part_no FROM tb3 WHERE part_no!=1 

4、使用视图

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

 select * from v1

三、存储过程

存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。(进一步提升了视图的简洁性和功能,使用方式类似于python中函数调用)

1、创建存储过程

 -- 创建存储过程

 delimiter //
create procedure p1()
BEGIN
select * from tb3;
END//
delimiter ; -- delimiter的作用是修改sql语句执行结束的判断标志,即若按照原来的方式,每当遇到 ; 则sql语句输入结束并运行,这样后面的END就不会生效,现先将执行结束的标志改为 // ,sql语句遇到//时才表示结束
 # ***************  终端调用  ****************
call p1() # ************* pymysql调用 *****************
import pymysql
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='cdc19951216', db='test',charset='utf8')
# 创建游标
cursor = conn.cursor()
cursor.callproc('p1') result = cursor.fetchone()
print(result) # 关闭游标
cursor.close()
# 关闭连接
conn.close()

调用存储过程

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

  • in          仅用于传入参数用
  • out        仅用于返回值用
  • inout     既可以传入又可以当作返回值
 -- 创建存储过程
delimiter \\
create procedure p1(
in i1 int, -- 可以理解为i1为整型参数,且只提供输入值的功能,使用前必须赋值
in i2 int,
inout i3 int, -- 可以理解为i3为整型参数,既要提供输入值又要提供返回值的功能,使用前必须赋值
out r1 int -- 可以理解为r1为整型参数,且只提供返回值的功能,开始时不必赋值,如果有值也会被后期覆盖掉
)
BEGIN
DECLARE temp1 int; -- 定义一个整型变量temp1
DECLARE temp2 int default 0; -- 定义一个整型变量temp2,初始值为0 set temp1 = 1; -- 赋值 set r1 = i1 + i2 + temp1 + temp2; -- r1 = 1+2+1+0 set i3 = i3 + 100; -- i3=4+100 end\\
delimiter ; -- 执行存储过程
set @t1 =4;
set @t2 = 0;
CALL p1 (1, 2 ,@t1, @t2);
SELECT @t1,@t2; -- 最后返回的是i3和r1的值

有参数的存储过程

 -- 创建存储过程
delimiter //
create procedure p1()
begin
select * from v1;
end //
delimiter ; -- 调用存储过程
call p1();

返回结果集

 -- 创建存储过程
delimiter //
CREATE PROCEDURE p2 ( IN n1 INT, INOUT n3 INT, OUT n2 INT, ) BEGIN
DECLARE
temp1 INT;
DECLARE
temp2 INT DEFAULT 0;
SELECT
*
FROM
v1; SET n2 = n1 + 100; SET n3 = n3 + n1 + 100; END //
delimiter; -- 调用存储过程
set @t1 = 3;
set @t2 = 5; -- 执行到这一步时,会将结果集打印出来,并将@t1和@t2的值分别传给n3和n2,但是不会还没有进行结果计算操作
call p2(1,@t1,@t2) -- 执行这一步才会进行运算并返回结果
select @t1,@t2

结果集+out值

 -- 存储过程与游标操作
delimiter //
CREATE PROCEDURE p3 ( ) BEGIN
DECLARE
ssid INT;-- 自定义变量1
DECLARE
ssname VARCHAR ( 50 );-- 自定义变量2
DECLARE
done INT DEFAULT FALSE;
DECLARE
my_cursor CURSOR FOR SELECT
sid,
sname
FROM
student;
DECLARE
CONTINUE HANDLER FOR NOT FOUND
SET done = TRUE;
OPEN my_cursor;
xxoo :
LOOP
FETCH my_cursor INTO ssid,
ssname;
IF
done THEN
LEAVE xxoo; END IF;
INSERT INTO teacher ( tname )
VALUES
( ssname ); END LOOP xxoo;
CLOSE my_cursor; END // delimter;

游标操作

 -- 动态执行sql可以防止sql注入
-- 创建存储过程
delimiter //
CREATE PROCEDURE p6( in num int)
BEGIN
set @age = num
PREPARE prod FROM 'select * from student where age>?'; -- prod相当于一个变量
EXECUTE prod USING @age; -- 字符串格式化
DEALLOCATE prepare prod; -- 执行sql语句
END//
delimiter ; -- 调用存储过程
call p6(18);

动态执行sql

2、删除存储过程

drop procedure proc_name;

 3、执行存储过程

 -- 无参数
call proc_name() -- 有参数,全in
call proc_name(1,2) -- 有参数,有in,out,inout
set @t1=0;
set @t2=3;
call proc_name(1,2,@t1,@t2)

终端

 # -*- coding:utf-8 -*-
import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='t1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 执行存储过程
cursor.callproc('p1', args=(1, 22, 3, 4))
# 获取执行完存储的参数
cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
result = cursor.fetchall() conn.commit()
cursor.close()
conn.close() print(result)

pymysql

四、触发器

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

1、创建触发器

 -- 插入前:即在执行向tb1表插入数据前先执行固定操作
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
...
END # 插入后:即在执行向tb1表插入数据后再执行固定操作
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_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
IF NEW. num = 666 THEN
INSERT INTO tb2 (NAME)
VALUES
(''),
('') ;
ELSEIF NEW. num = 555 THEN
INSERT INTO tb2 (NAME)
VALUES
(''),
('') ;
END IF;
END //
delimiter ;

插入后触发

 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')
ENDIF;
END //
delimiter ;

插入前触发

注:NEW表示用户新输入的原来没有的数据,OLD表示即将删除的原来有的数据。

-- 删除student中的姓名为alex的数据,并在删除操作前将删除的姓名记录到tb3中

delimiter //
CREATE TRIGGER tri_before_insert_tb3 AFTER INSERT ON tb3 FOR EACH ROW
BEGIN
insert into tb3(name) values(OLD.sname);
ENDIF;
END //
delimiter ;
-- 将原来student中学号为1的sname记录到tb3中,再将该记录的sname更新为alex

delimiter //
CREATE TRIGGER tri_before_delete_student BEFORE DELETE ON student FOR EACH ROW
BEGIN
insert into tb3(name) values(OLD.sname);
insert into student(sname) values(NEW.sname) where sid=1;
ENDIF;
END //
delimiter ; -- 若原数据:sname:cdc sid:1
-- 执行update student set name='alex' where sid=1; OLD.sname为cdc,NEW.sname为alex

2、删除触发器

DROP TRIGGER tri_after_insert_tb1;

 五、函数

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,则返回 NULL 。
REPLACE(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、自定义函数

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

3、删除函数

drop function func_name;

4、执行函数

 # 获取返回值
declare @i VARCHAR(32);
select UPPER('alex') into @i;
SELECT @i; # 在查询中使用
select f1(11,nid) ,name from tb2;

5、函数与存储过程的比较

存储过程:a) 可以执行sql语句

     b) 只能通过out和inout来构造返回值

函数:      a) 不能执行sql语句,除赋值语句:(例) select nid into a from student where sname='cdc';

     b) 可以直接通过return返回

 六、循环判断

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 ;

if

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 ;

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 ;

repeat

 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

loop

 六、慢日志记录和查询

通过设置日志相关信息,可以查到查询较慢、是否使用索引等相关操作的日志记录。

1、在配置文件中设置慢日志

slow_query_log = OFF                            是否开启慢日志记录
long_query_time = 2                              时间限制,超过此时间,则记录
slow_query_log_file = /usr/slow.log        日志文件
log_queries_not_using_indexes = OFF     为使用索引的搜索是否记录

2、在终端设置慢日志

查看当前配置信息:show variables like '%query%'

设置变量值:set 变量名=值

重启服务器,带上日志文件存储路径

完结撒花,掰掰 !!!

Python学习—数据库篇之SQL补充的更多相关文章

  1. Python学习—数据库篇之SQL语句

    一.数据库级别 1.显示数据库 show databases; 默认数据库: mysql - 用户权限相关数据 test - 用于用户测试数据 information_schema - MySQL本身 ...

  2. Python学习—数据库篇之索引

    一.索引简介 索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构.类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可,对于索引,会保存在额外的文件中.在mys ...

  3. Python学习—数据库篇之pymysql

    一.pymysql简介 对于Python操作MySQL主要使用两种方式: 原生模块 pymsql ORM框架 SQLAchemy pymsql是Python中操作MySQL的模块,其使用方法和MySQ ...

  4. Python学习—数据库篇之初识mysql

    一.下载与安装 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司.MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好 ...

  5. Python学习—数据库篇之练习题

    Mysql测试题 一.表关系 请创建如下表,并创建相关约束 二.操作表 0.在成绩表中同时显示出对应的课程名和学生名 1.自行创建测试数据 2.查询“生物”课程比“物理”课程成绩高的所有学生的学号: ...

  6. [Python学习]错误篇二:切换当前工作目录时出错——FileNotFoundError: [WinError 3] 系统找不到指定的路径

    REFERENCE:<Head First Python> ID:我的第二篇[Python学习] BIRTHDAY:2019.7.13 EXPERIENCE_SHARING:解决切换当前工 ...

  7. python 操作数据库的常用SQL命令

    这俩天在学习PYTHON操作数据库的知识.其实基本SQL命令是与以前学习的MYSQL命令一致,只是增加了一些PYTHON语句. 1,安装pymysql,并导入. import pymysql 2,因为 ...

  8. Python学习---Django下的Sql性能的测试

    安装django-debug-tools Python学习---django-debug-tools安装 性能测试: settings.py INSTALLED_APPS = [ ... 'app01 ...

  9. Python学习第一篇

    好久没有来博客园了,今天开始写自己学习Python和Hadoop的学习笔记吧.今天写第一篇,Python学习,其他的环境部署都不说了,可以参考其他的博客. 今天根据MachineLearning里面的 ...

随机推荐

  1. Windows下安装Spark环境

    根据博客总结 https://blog.csdn.net/nxw_tsp/article/details/78281533 需要的安装软件可以在网盘下载: 链接:https://pan.baidu.c ...

  2. Web前端数据存储

    Cookie 会跟随每次请求附加到请求header上,大小限制4k. 部署对象: document.cookie 构成: Key-Value 有效期:根据expires配置 可选项: ;path=pa ...

  3. Presto 性能优化点

    1.指定需要返回的字段 [GOOD]: SELECT time,user,host FROM tbl[BAD]: SELECT * FROM tbl 2.合理设置分区字段 当过滤条件作用在分区字段上面 ...

  4. htm,css,javascript及其他的注释方式

    转自:http://www.cnblogs.com/dapeng111/archive/2012/12/23/2829774.html 一.HTML的注释方法<!-- html注释:START ...

  5. Oracle 巡检命令

    [grid@XXXXX ~]$ asmcmd -pASMCMD [+] > lsdg

  6. LeetCode 789. Escape The Ghosts

    题目链接:https://leetcode.com/problems/escape-the-ghosts/description/ You are playing a simplified Pacma ...

  7. java生成word

    import freemarker.template.Configuration; import freemarker.template.Template; import javax.servlet. ...

  8. 【HQL】函数汇总

    背景 抽空整理一篇HQL函数及常用的小技巧 COALESCE COALESCE(T v1, T v2, -) 返回参数中的第一个非空值:如果所有值都为NULL,那么返回NULL

  9. sbt 学习

    一.基础 1.工程根目录 包含build.sbt的目录是工程的根目录.注意,就算在一个空目录下面执行sbt about,也会生成project文件夹 2.源文件目录结构 SBT有固定的文件组织结构 s ...

  10. jersey 开启gzip

    @Bean public ResourceConfig resourceConfig() { ResourceConfig resourceConfig = new ResourceConfig(); ...