一、pymysql模块的使用

1、pymysql的下载和使用

之前我们都是通过MySQL自带的命令行客户端工具mysql来操作数据库,那如何在python程序中操作数据库呢?这就需要用到pymysql模块,该模块本质就是一个套接字客户端软件,使用前需要事先安装。

1)pymysql模块的下载

    pip3 install pymysql

2)pymysql模块的使用

现有数据库mydb,其中有一个userinfo表,表中数据如下:

    mysql> select * from userinfo;
+----+------+-----+
| id | name | pwd |
+----+------+-----+
| 1 | wll | 123 |
| 2 | ssx | 456 |
+----+------+-----+

示例:使用Python实现用户登录,如果用户存在则登录成功,否则登录失败

    import pymysql
username = input('请输入用户名:')
pwd = input('请输入密码:') # 1、连接
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = '',
db = 'mydb',
charset = 'utf8'
)
# 2、创建游标
cur = conn.cursor() sql = "select * from userinfo where name='%s' and pwd='%s'" %(username,pwd)
# 3、执行sql语句
result = cur.execute(sql)
print(result) # result为sql语句执行后生效的行数 # 4、关闭:游标和连接都要关闭
cur.close()
conn.close() if result:
print('登录成功')
else:
print('登录失败')
2、execute()之sql注入问题

  sql语句的注释:-- 这是注释

  一条sql语句如果是select * from userinfo where name='wll' -- haha' and pwd=''

  那么-- 之后的内容就被注释掉了(注意:--后面还有一个空格)。

所以,上例中当用户输入如下内容就会出现问题

# sql注入之:用户存在,绕过密码

wll' -- 任意字符

  # sql注入之:用户不存在,绕过用户名和密码

  xxx' or 1=1 -- 任意字符

  原因是我们对sql语句进行字符串拼接时,为%s加了引号,解决方法如下:

    # 用execute()帮我们做字符串拼接,无需且一定不能再为%s加引号(因为pymysql会自动加上)
sql = "select * from userinfo where name=%s and pwd=%s"
result = cur.execute(sql,[username,pwd]) # 第二个参数可以是列表
result = cur.execute(sql,(username,pwd)) # 也可以是元组 # 当execute()的第二个参数是字典时,sql中应该加上key,如下
sql = "select * from userinfo where name=%(key1)s and pwd=%(key2)s"
result = cur.execute(sql,{'key1':username,'key2':pwd})
3、pymysql中对数据库增、删、改:conn.commit()

  commit()方法:通过pymysql对数据库进行增、删、改时,必须用commit()方法提交,否则无效。

示例:

    import pymysql
username = input('请输入用户名:')
pwd = input('请输入密码:') # 1、连接
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = '',
db = 'mydb',
charset = 'utf8'
)
# 2、创建游标对象
cur = conn.cursor() # 3、执行sql语句
# 增
sql = "insert into userinfo(name,pwd) values (%s,%s)"
result = cur.execute(sql,[username,pwd])
print(result) # 输出 1
# 同时插入多条数据
effect_row = cur.executemany(sql,[('张三',''),('李四','')])
print(effect_row) # 输出 2 # 删
sql = "delete from userinfo where id=1"
effect_row = cur.execute(sql)
print(effect_row) # # 改
sql = "update userinfo set name=%s where id=2"
effect_row = cur.execute(sql,username)
print(effect_row) # # 4、增、删、改之后一定要commit
conn.commit() # 5、关闭:游标和连接都要关闭
cur.close()
conn.close()
4、pymysql中对数据库查询:fetchone()、fetchall()、fetchmany(n)

有如下表内容:

    mysql> select * from userinfo;
+----+--------+-----+
| id | name | pwd |
+----+--------+-----+
| 1 | wll | 123 |
| 2 | ssx | 456 |
| 3 | 张三 | 123 |
| 4 | 李四 | 456 |
+----+--------+-----+

示例一:fetchone() – 获取一行数据,第一次为首行

    import pymysql
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='mydb', charset='utf8') # 此处不能写 utf-8 ,否则报错
cur = conn.cursor()
sql = "select * from userinfo"
effct_row = cur.execute(sql)
print(effct_row) # row = cur.fetchone() # 查询第一行的数据
print(row) # (1, 'wll', '123')
row = cur.fetchone() # 从上次位置继续,即查询第二行数据
print(row) # (2, 'ssx', '456')
cur.close()
conn.close()

示例二:fetchall() - 获取所有行数据

    import pymysql
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='mydb', charset='utf8')
cur = conn.cursor()
sql = "select * from userinfo"
effct_row = cur.execute(sql)
print(effct_row) # rows = cur.fetchall() # 查询所有行的数据
print(rows)
# 结果为:
# ((1, 'wll', '123'), (2, 'ssx', '456'), (3, '张三', '123'), (4, '李四', '456'))
cur.close()
conn.close()

总结:从上例中输出结果可以看出,我们获取到的返回值是一个元组,每一行数据也是一个元组,所以我们无法知道数据对应的字段是什么,这个时候,可以通过如下方式将每一行的数据变为一个字典,字典的key就是字段名,value就是对应的值,如下:

    # 在实例化游标对象的时候,将属性cursor设置为    pymysql.cursors.DictCursor
cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 结果为:
# [
# {'id': 1, 'name': 'wll', 'pwd': '123'},
# {'id': 2, 'name': 'ssx', 'pwd': '456'},
# {'id': 3, 'name': '张三', 'pwd': '123'},
# {'id': 4, 'name': '李四', 'pwd': '456'}
# ]

示例三:移动指针位置

fetchone示例中,在获取行数据的时候,可以理解为,刚开始,有一个行指针指着第一行的上方,获取一行,它就向下移动一行,所以当行指针移到最后一行的时候,就不能再获取到内容,所以我们可以使用如下方法来移动行指针:

    cur.scroll(1,mode='relative')  # 相对当前位置移动
cur.scroll(1,mode='absolute') # 相对首行位置移动

参数解释:

第一个值为移动的行数,正数为向下移动,负数为向上移动;mode指定了是相对当前位置移动,还是相对于首行移动。

代码:

    import pymysql
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='mydb', charset='utf8')
cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
sql = 'select * from userinfo'
effct_row = cur.execute(sql) row = cur.fetchone() # 查询第一行的数据
print(row) # {'id': 1, 'name': 'wll', 'pwd': '123'}
row = cur.fetchone() # 查询第二行数据
print(row) # {'id': 2, 'name': 'ssx', 'pwd': '456'} cur.scroll(-1,mode='relative')
row = cur.fetchone()
print(row) # {'id': 2, 'name': 'ssx', 'pwd': '456'} cur.scroll(0,mode='absolute')
row = cur.fetchone()
print(row) # {'id': 1, 'name': 'wll', 'pwd': '123'} cur.close()
conn.close()

示例四:fetchmany(n) - 获取n行数据

    import pymysql
conn = pymysql.connect(host='localhost', port=3306, user='root', password='', db='mydb', charset='utf8')
cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
sql = 'select * from userinfo'
effct_row = cur.execute(sql) rows = cur.fetchmany(2) # 获取2 条数据
print(rows)
# 结果为:
# [
# {'id': 1, 'name': 'wll', 'pwd': '123'},
# {'id': 2, 'name': 'ssx', 'pwd': '456'}
# ]
cur.close()
conn.close()

二、索引

1、索引的介绍

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

2、索引的作用

约束和加速查找。

3、常见的几种索引

1)普通索引:加速查找作用

示例一:创建表的时候设置普通索引

    create table userinfo(
id int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
index ix_name(name) # 设置普通索引
);

示例二:已经创建完表之后单独创建普通索引(慢)

    create index 索引的名字 on 表名(列名);

示例三:删除普通索引(快)

    drop index 索引名 on 表名;

示例四:查看索引

    show index from 表名;

2)唯一索引:加速查找和约束唯一作用(可以为空)

示例一:创建表的时候设置唯一索引

    create table userinfo(
id int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
unique index ix_name(name) # 设置唯一索引(name就有了唯一的约束)
);

示例二:单独设置唯一索引

    create unique index 索引名 on 表名(列名);

示例三:删除唯一索引

    drop index 索引名 on 表名;

3)主键索引:加速查找和约束唯一作用(不可以为空)

  示例一:创建表的时候设置主键索引

    create table userinfo(
id int not null auto_increment primary key, # 设置主键就是主键索引
name varchar(32) not null,
email varchar(64) not null,
);
或者
create table userinfo(
id int not null auto_increment,
name varchar(32) not null,
email varchar(64) not null,
primary key(id) # 设置主键,就创建主键索引
);

  示例二:单独创建主键索引

    alter table 表名 add primary key(列名);

  示例三:删除主键索引

    alter table 表名 drop primary key;
alter table 表名 modify 列名 int, drop primary key;

4)联合索引(多列)

又分为:联合主键索引、联合唯一索引、联合普通索引。

应用场景:频繁的同时使用n列来进行查询,

如:where name = ‘alex’and email = ‘alex@qq.com’;

示例一:创建联合普通索引

    create index 索引名 on 表名(列名1,列名2);
4、覆盖现象和合并现象

示例一:查找字段和索引字段相同,则直接在索引文件中获取数据

    select name from userinfo where name = 'alex50000';  # 直接索引文件中获取
select * from userinfo where name = 'alex50000'; # 先查索引文件,再查物理表

示例二:多个单列索引同时作为条件时,索引则合并使用

    select * from  userinfo where name = 'alex13131' and id = 13131;
5、如何正确使用索引

数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。

使用索引,我们必须遵循以下几点:

1)创建索引;

2)命中索引;

3)正确使用索引;

准备一个含有300w数据的表:

    # 1. 准备表
create table userinfo(
id int,
name varchar(20),
gender char(6),
email varchar(50)
); # 2. 创建存储过程,实现批量插入记录
delimiter $$ # 声明存储过程的结束符号为$$
create procedure auto_insert1()
BEGIN
  declare i int default 1;
  while(i<3000000)do
    insert into userinfo values(i,concat('alex',i),'male',concat('egon',i,'@oldboy'));
    set i=i+1;
  end while;
END$$ # $$结束
delimiter ; # 重新声明分号为结束符号 # 3. 查看存储过程
show create procedure auto_insert1\G; # 4. 调用存储过程
call auto_insert1();

测试如下查询语句,体会以下不正确使用索引的情况,理解如何正确使用索引:

    # 示例一:like '%xx'
select * from userinfo where name like '%al'; # 示例二:使用函数
select * from userinfo where reverse(name) = 'alex333'; # 示例三:or
select * from userinfo where id = 1 or email = 'alex122@oldbody';
# 注意:当or条件中有未建立索引的列才失效,以下两种会走索引:
select * from userinfo where id = 1 or name = 'alex1222';
select * from userinfo where id = 1 or email = 'alex122@oldbody' and name = 'alex112' # 示例四:类型不一致
select * from userinfo where name = 999; # 表中name字段是字符串
# 解释:若某字段是字符串类型,则查询条件中必须带引号,否则即使该字段有索引,速度也很慢 # 示例五:!=
select count(*) from userinfo where name != 'alex';
# 注意:如果是主键,则还是会走索引 # 示例六:>
select * from userinfo where name > 'alex';
# 注意:如果是主键或者字段是整数类型,则还是会走索引,如下:
select * from userinfo where id > 123
select * from userinfo where num > 123 # 示例七:order by
select email from userinfo order by name desc;
# 注意:当根据索引排序的时候,选择的映射如果不是索引,则不走索引 # 示例八:联合索引最左前缀匹配

PS:什么是最左前缀匹配?

  create index ix_name_email on userinfo(name,email);  # 创建联合索引,name在左

  select * from userinfo where name = 'alex';                 # 查找速度快

  select * from userinfo where name = 'alex' and email='alex@oldBody';  # 快

  select * from userinfo where  email='alex@oldBody';    # 慢

分析:如果创建了联合索引,如上边代码,创建name和email联合索引,那么查询

(1)name和email时 -- 使用索引,速度快

(2)name            -- 使用索引,速度快

(3)email            -- 不使用索引,速度慢

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

6、使用索引的注意事项

1)避免使用select *;

  2)count(1)或count(列) 代替count(*);

  3)创建表时尽量使用char代替varchar;

  4)表的字段顺序固定长度的字段优先;

  5)组合索引代替多个单列索引(经常使用多个条件查询时);

  6)尽量使用短索引(create index ix_title on tb(title(16));仅限特殊的数据类型text);

  7)使用连接(join)来代替子查询;

  8)连表时注意条件类型需一致;

  9)索引散列(有重复且种类少)不适用于建索引,例如:性别不合适;

7、执行计划

explain + 查询SQL :用于显示SQL执行信息参数,根据参考信息可以进行SQL优化。如下示例:

  mysql> explain select * from userinfo;
  +----+-------------+----------+------+---------------+------+---------+------+---------+-------+
  | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  +----+-------------+----------+------+---------------+------+---------+------+---------+-------+
  | 1 | SIMPLE | userinfo | ALL | NULL | NULL | NULL | NULL | 2973016 | NULL |
  +----+-------------+----------+------+---------------+------+---------+------+---------+-------+   mysql> explain select * from (select id,name from userinfo where id <20) as A;
  +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
  | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
  | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 19 | NULL |
  | 2 | DERIVED | userinfo | range | PRIMARY | PRIMARY | 4 | NULL | 19 | Using where |
  +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+

参数说明:

    select_type(查询类型):
SIMPLE --- 简单查询
PRIMARY --- 最外层查询
SUBQUERY --- 映射为子查询
DERIVED --- 子查询
UNION --- 联合
UNION RESULT --- 使用联合的结果
table(正在访问的表名)
type(查询时的访问方式):
性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
all --- 全表扫描,对于数据表从头到尾找一遍(如果有limit限制,则找到之后不再向下找);
index --- 全索引扫描,对索引从头到尾找一遍;
range --- 对索引列进行范围查找;
index_merge --- 合并索引,使用多个单列索引搜索;
ref --- 根据索引查找一个或多个值;
eq_ref --- 连接时使用primary key或unique类型;
system --- 系统,表仅有一行(=系统表),这是const连接类型的一个特例;
const --- 常量,表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次;
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、慢日志记录

开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。

1)进入MySQL查询是否开启了慢查询日志

    show variables like 'slow_query%';

  参数解释:

      slow_query_log:慢查询开启状态(OFF未开启,ON为开启);

      slow_query_log_file:慢查询日志存放的位置;

2)查看慢查询超时时间(默认10秒)

    show variables like 'long%';

3)开启慢日志方式一:

    set global slow_query_log=1;   # 1表示开启,0表示关闭

注意:设置关要退出重新进入才生效。

4)开启慢日志方式二(推荐):

修改my.ini配置文件(mac中为my.cnf文件),找到[mysqld],在下面添加:

    slow_query_log = 1
slow_query_log_file=C:\mysql-5.6.40-winx64\data\localhost-slow.log
long_query_time = 1

参数解释:

      slow_query_log:慢查询开启状态,1为开启

      slow_query_log_file:慢查询日志存放的位置

      long_query_time:查询超过多少秒才记录,默认10秒,修改为1秒

9、分页性能相关方案

  先回顾一下,如何取当前表中的前10条记录,每十条取一次,依次得到每页数据,如下:

    # 第1页:
select * from userinfo limit 0,10;
# 第2页:
select * from userinfo limit 10,10;
# 第3页:
select * from userinfo limit 20,10;
# 第4页:
select * from userinfo limit 30,10;
......
# 第200001页
select * from userinfo limit 2000000,10;

  PS:我们会发现,越往后查询,需要的时间约长,此方法要进行全文扫描查询,越往后查,扫描查询的数据越多。

解决方案:

1)只有上一页和下一页的情况

前提:做一个记录,记录当前页的第一条数据min_id或者最后一条数据max_id

    # 下一页
select * from userinfo where id>max_id limit 10;
# 上一页
select * from userinfo where id<min_id order by id desc limit 10;

2)中间有页码的情况

    select * from userinfo where id in(
select id from (select * from userinfo
where id > pre_max_id limit (cur_max_id-pre_max_id)*10) as A
order by A.id desc limit 10
);

MySQL数据库(5)- pymysql的使用、索引的更多相关文章

  1. Mysql(九):Python连接MySQL数据库之pymysql模块使用

    Python3连接MySQL 本文介绍Python3连接MySQL的第三方库--PyMySQL的基本使用. PyMySQL介绍 PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服 ...

  2. 使用第三方库连接MySql数据库:PyMysql库和Pandas库

    使用PyMysql库和Pandas库链接Mysql 1 系统环境 系统版本:Win10 64位 Mysql版本: 8.0.15 MySQL Community Server - GPL pymysql ...

  3. 38、mysql数据库(pymysql及事务)

    38.1.python之pymysql模块: 1.说明: pymsql是Python中操作MySQL的模块,其使用方法和py2的MySQLdb几乎相同. 2.模块安装: pip install pym ...

  4. Python 3.5 连接Mysql数据库(pymysql 方式)

    由于 MySQLdb 模块还不支持 Python3.x,官方的Mysql连接包只支持到3.4,所以 Python3.5 如果想连接MySQL需要安装 pymysql 模块. pymysql 模块可以通 ...

  5. Python连接MySQL数据库之pymysql模块使用

    安装PyMySQL pip install pymysql PyMySQL介绍 PyMySQL是在python3.x版本中用于连接MySQL服务器的一个库,2中则使用mysqldb. Django中也 ...

  6. Python连接MySQL数据库之pymysql模块

    pymysql 在python3.x 中用于连接MySQL服务器的一个库:Python2中则使用mysqldb pymysql的模块的基本的使用 # 导入pymysql模块 import pymysq ...

  7. Python和MySQL数据库交互PyMySQL

    Python数据库操作 对于关系型数据库的访问,Python社区已经指定了一个标准,称为Python Database API SepcificationV2.0.MySQL.Qracle等特定数据库 ...

  8. 面试题: mysql 数据库已看 sql安全性 索引 引擎 sql优化

    总结的一些MySQL数据库面试题 2016年06月16日 11:41:18 阅读数:4950 一.sql语句应该考虑哪些安全性? (1)防止sql注入,对特殊字符进行转义,过滤或者使用预编译的sql语 ...

  9. python 连接mysql数据库:pymysql

    示例:import pymysql conn=pymysql.connect( host="127.0.0.1", #数据库IP port=3306, #数据库端口 user=&q ...

  10. MySQL数据库几种常用的索引类型使用介绍

    一.简介 MySQL目前主要有以下几种索引类型: 1.普通索引 2.唯一索引 3.主键索引 4.组合索引 5.全文索引 二.语句 1 2 CREATE TABLE table_name[col_nam ...

随机推荐

  1. ajax发送请求时为url添加参数(使用函数)

    <script> // ajax的get请求,使用函数向其url添加参数 function addURLParam(url,name,value){ url+=(url.indexOf(' ...

  2. anaconda指定镜像源,解决conda下载速度慢失败问题

    conda升级默认官网地址,速度会特别慢,现在我们指定一个当前可用的镜像,步骤如下: 1.执行命令,生成.condarc文件 conda config --add channels https://m ...

  3. iOS学习笔记10 - Bundle和Info.plist

    经常会有需要从应用中搜索并读取一个文件或图片.这时候就会用到如下的语句: NSString *path = [[NSBundle mainBundle] pathForResource:@" ...

  4. flask 中xx.init_app(app)方法

    bootstrap = Bootstrap() mail = Mail() moment = Moment() db = SQLAlchemy() def create_app(config_name ...

  5. 流媒体服务器开发笔记(2)--RTCP协议介绍

    http://blog.sina.com.cn/s/blog_53061af00100o2no.html ——————————————————————————————————————————————— ...

  6. pl/sql 实例精解 05

    本章主要讨论 case 语句 1: case [SELECTOR] 2: when expression 1 then statement 1; 3: when expression 2 then s ...

  7. J2EE是什么?

    解答:从整体上讲,J2EE是使用Java技术开发企业级应用的工业标准,它是Java技术不断适应和促进企业级应用过程中的产物.适用于企业级应用的J2EE,提供一个平台独立的.可移植的.多用户的.安全的和 ...

  8. ==和Equal()的区别

    我们在编程的时候,经常会遇到判断两个对象是否相等的情况.说到判断两个对象是否相等,就不得不说对象的类型和对象在内存中的存储情况. 对象类型可以分为值类型和引用类型: 值类型包括:简单类型.结构类型.枚 ...

  9. landa语法

    sg_orm看当前sql信息 db.IsEnableLogEvent = true; db.LogEventStarting = (sql, pa) => { var t = 0; }; //出 ...

  10. 【ARIMA】Autoregressive Integrated Moving Average Model

    [理论部分] ARIMA包含两部分,自回归AR和移动平均MA: AR:Y(t)-a=b(1){Y(t-1)-a}+u(t)   其中a是y的均值, u(t)是均值为零,恒定方差的不相关随机误差项(噪声 ...