本文主要目的就是对数据库的优化查询,但查询过程中碰到了一个不明缘由的问题,希望有大神能够帮忙解释一波。
另:不支持图片外链,我没找到什么好方法来上传图片,如果有需要看图片的,可以移步到我的博客中去看,本篇文章的博客地址在本文末。
目的
从 csv 文件中随机选取 100 条 Ip 地址,并在表中查询这 100条 ip 的所在地。
步骤
- 读取样本,组装列表
- 表建立索引
- 优化 sql 语法
- 联合查询
读取文件
读取文件和上一篇文章所讲的一样,利用 with
语法避免文件句柄关闭操作,给 open
函数增加 encoding 参数。
然后将读取的数据进行拆分和整理:
with open('./ipdata.csv', 'r', encoding='utf-8') as f:
lines = f.readlines()
nl_p_list = []
for l in lines:
ls = l.strip().split(',', 4)
c1, c2, c3, c4, c5 = ls[0], ip2int(ls[1]), ip2int(ls[2]), ls[3], ls[4]
nl = [c1, c2, c3, c4, c5]
nl_p_list.append(nl)
表建立索引
使用 navicat 连接表,进入 「设计表」 模块,为 startip 和 endip 建立索引。
[图片上传失败...(image-c12667-1518151013339)]
优化 sql 语法
如果使用复杂语法的 sql 语句,是无法利用索引大幅提升查询效率的,比如通过 between and 查询:
SELECT * FROM ipdata WHERE IpNum BETWEEN startip AND endip
因为 ip 是连续的,所以只要这个 Ip 地址大于或等于某一个号段的起始地址,那么依照降序排序第一的 ip 段的所在地,就是被查询的 ip 地址的所在地。
SELECT * FROM ipdata WHERE IpNum >= startip ORDER BY startip DESC LIMIT 1
经过对比发现,查询速度果然是有质的飞跃。但也出现了问题。问题详情我会在文末写明。
联合查询
利用索引的 sql 语句果然是如有神助,在我的运行环境里, between and 查询语句,查询 100 条记录需要 32s+ ,但在优化后能提升到 0.7s 左右。
但是在使用联合查询后,还能提升将近 2 倍的速率。
联合查询的 sql 语法:
SELECT t1.* FROM (SELECT * FROM ipdata WHERE 1780997668 >= startip ORDER BY startip DESC LIMIT 1) t1
UNION ALL
SELECT t2.* FROM (SELECT * FROM ipdata WHERE 3425736747 >= startip ORDER BY startip DESC LIMIT 1) t2;
下一步,就是加工 sql 联合语句了。联合查询的 sql 语句,不宜过长,比较适合的长度就是 100 条。
sql_str = 'SELECT {0}.* FROM (SELECT * FROM ipdata WHERE %s >= startip ORDER BY startip DESC LIMIT 1) {0}'
for i in range(len(ip_list)):
sql_list.append(sql_str.format('t' + str(i)) % ip_list[i])
sql = ' union all '.join(sql_list)
使用 format 函数填充别名, %s 填充 IP 地址 。
最后执行 sql 语句:
cursor.execute(sql)
效果也是有的,将查询的速度提升到了 0.3s 左右。
至此, sql 的数据表查询优化部分那就结束了。
遇到的灵异现象
如图所示,我注释了 t1, t2, t3, t4 之间的代码。
[图片上传失败...(image-f064cf-1518151013339)]
时间段 | 含义 | 运行速度 |
---|---|---|
t0 ~ t1 | 联合优化 sql 语句查询 | 0.0318 |
t1 ~ t2 | 优化 sql 语句查询 | 9.5367 |
t3 ~ t2 | between and 语句查询 | 1.1920 |
t4 ~ t3 | 联合 between and 语句查询 | 0.0 |
事实上,除了 t0 ~ t1 之间的时间是大致在 0.03s ~ 0.07s 波动,剩余的三个时间段的代码已经被我注释,理论上应该是不耗费时间的,但会随机在剩余的三个时间段 打印出 9s 左右的耗时。希望有大神能够帮助一波。
源码地址:
『NiuCodeLesson/insertSQL/』:https://github.com/wengfe/NiuCodeLesson/tree/master/insertSQL
博客地址:
『wengfe.win』:http://www.wengfe.win/2018/02/07/2018-02-08/#more