使用Python内置模块加速SQL查询

大家好,假设你正在查阅一本书的页面,你想要更快地找到你正在寻找的信息。那么你可能会查找术语索引,然后跳转到引用特定术语的页面,SQL中的索引与书籍中的索引工作原理类似。

在大多数实际系统中,都将对包含大量行的数据库表运行查询(想象一下数百万行),需要通过扫描所有行来检索结果的查询将非常慢。如果你知道经常需要根据某些列查询信息,可以在这些列上创建数据库索引,这将大大加快查询速度。

本文将介绍如何使用sqlite3模块在Python中连接和查询SQLite数据库,同时还将讲述如何添加索引并看到它是如何提高性能的。 

在Python中连接到数据库

本文将使用内置的sqlite3模块。在开始运行查询之前,需要做到以下步骤:

【sqlite3】:https://docs.python.org/3/library/sqlite3.html

  • 连接到数据库

  • 创建一个数据库游标以运行查询

要连接到数据库,本文将使用sqlite3模块中的connect()函数。一旦建立了连接,就可以在连接对象上调用cursor()来创建一个数据库游标,如下所示:

import sqlite3# 连接到数据库
db_conn = sqlite3.connect('people_db.db')
db_cursor = db_conn.cursor()

在这里,尝试连接到名为people_db的数据库。如果数据库不存在,运行上述代码片段将为我们创建SQLite数据库。

创建表格并插入记录

现在,本文将在数据库中创建一个表,并向其中添加记录。

people_db数据库中创建一个名为people的表,其中包含以下字段:

  • name

  • email

  • job

# main.py
...
# 创建表格
db_cursor.execute('''CREATE TABLE people (id INTEGER PRIMARY KEY,name TEXT,email TEXT,job TEXT)''')...# 提交事务,关闭游标和数据库连接
db_conn.commit()
db_cursor.close()
db_conn.close()

现在,需要在表中插入记录。为此将使用Faker——一个用于生成合成数据的Python软件包,可以通过pip安装:

$ pip install faker

安装Faker后,就可以将Faker类导入到Python脚本中:

# main.py
...
from faker import Faker
...

下一步是生成并插入people表中的记录。为了演示索引如何加快查询速度,本文将插入大量记录。在这里将插入10万条记录;将num_records变量设置为100000

然后执行以下操作:

  • 实例化一个Faker对象fake并设置种子以获得可复现性。

  • 使用first_name()last_name()fake对象上调用,获取一个名字字符串。

  • 通过调用domain_name()生成一个虚假域名。

  • 使用名字和域名生成电子邮件字段。

  • 使用job()为每个个体记录获取一个职位。

使用如下代码生成并插入people表中的记录:

# 创建并插入记录
fake = Faker() # 确保导入:from faker import Faker 
Faker.seed(42)num_records = 100000for _ in range(num_records):first = fake.first_name()last = fake.last_name()name = f"{first} {last}"domain = fake.domain_name()email = f"{first}.{last}@{domain}"job = fake.job()db_cursor.execute('INSERT INTO people (name, email, job) VALUES (?,?,?)', (name,email,job))# 提交事务并关闭游标和数据库连接
db_conn.commit()
db_cursor.close()
db_conn.close()

现在,main.py文件的包含代码如下:

# main.py
# 导入
import sqlite3
from faker import Faker# 连接到数据库
db_conn = sqlite3.connect('people_db.db')
db_cursor = db_conn.cursor()# 创建表格
db_cursor.execute('''CREATE TABLE people (id INTEGER PRIMARY KEY,name TEXT,email TEXT,job TEXT)''')# 创建并插入记录
fake = Faker()
Faker.seed(42)num_records = 100000for _ in range(num_records):first = fake.first_name()last = fake.last_name()name = f"{first} {last}"domain = fake.domain_name()email = f"{first}.{last}@{domain}"job = fake.job()db_cursor.execute('INSERT INTO people (name, email, job) VALUES (?,?,?)', (name,email,job))# 提交事务并关闭游标和数据库连接
db_conn.commit()
db_cursor.close()
db_conn.close()

运行此脚本一次,在表中填入记录数num_records

查询数据库

现在本文有了包含10万条记录的表格,接下来在people表格上运行一个示例查询。

通过运行一个查询来:

  • 获取职位为“产品经理”的记录的姓名和电子邮件,并将查询结果限制为10条记录。

本文将使用time模块的默认计时器来获取查询的大致执行时间。

# sample_query.pyimport sqlite3
import timedb_conn = sqlite3.connect("people_db.db")
db_cursor = db_conn.cursor()t1 = time.perf_counter_ns()db_cursor.execute("SELECT name, email FROM people WHERE job='Product manager' LIMIT 10;")res = db_cursor.fetchall()
t2 = time.perf_counter_ns()print(res)
print(f"Query time without index: {(t2-t1)/1000} us")

以下是输出结果:

Output >>
[("Tina Woods", "Tina.Woods@smith.com"),("Toni Jackson", "Toni.Jackson@underwood.com"),("Lisa Miller", "Lisa.Miller@solis-west.info"),("Katherine Guerrero", "Katherine.Guerrero@schmidt-price.org"),("Michelle Lane", "Michelle.Lane@carr-hardy.com"),("Jane Johnson", "Jane.Johnson@graham.com"),("Matthew Odom", "Matthew.Odom@willis.biz"),("Isaac Daniel", "Isaac.Daniel@peck.com"),("Jay Byrd", "Jay.Byrd@bailey.info"),("Thomas Kirby", "Thomas.Kirby@west.com"),
]Query time without index: 448.275 us

还可以通过在命令行中运行sqlite3 db_name来调用SQLite命令行客户端:

$ sqlite3 people_db.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.

要获取索引列表,可以运行.index

sqlite> .index

由于当前没有索引,因此不会列出任何索引。

还可以像这样检查查询计划:

sqlite> EXPLAIN QUERY PLAN SELECT name, email FROM people WHERE job='Product Manager' LIMIT 10;
QUERY PLAN
`--SCAN people

这里的查询计划是扫描所有行,效率不高。

在特定列上创建索引

要在特定列上创建数据库索引,可以使用以下语法:

CREATE INDEX index-name on table (column(s))

假设需要经常查找具有特定职位的个人记录,在职位列上创建一个名为people_job_index的索引有助于提高效率:

# create_index.pyimport time
import sqlite3db_conn = sqlite3.connect('people_db.db')db_cursor =db_conn.cursor()t1 = time.perf_counter_ns()db_cursor.execute("CREATE INDEX people_job_index ON people (job)")t2 = time.perf_counter_ns()db_conn.commit()print(f"Time to create index: {(t2 - t1)/1000} us")Output >>
Time to create index: 338298.6 us

尽管创建索引需要这么长时间,但这是一次性的操作。在运行多个查询时,仍然会获得相当大的加速。

现在如果在SQLite命令行客户端运行.index,将获得:

sqlite> .index
people_job_index

使用索引查询数据库

如果现在查看查询计划,应该能够看到现在使用名为people_job_index的索引在job列上搜索people表:

sqlite> EXPLAIN QUERY PLAN SELECT name, email FROM people WHERE job='Product manager' LIMIT 10;
QUERY PLAN
`--SEARCH people USING INDEX people_job_index (job=?)

可以重新运行sample_query.py。仅修改print()语句,然后看看现在运行查询需要多长时间:

# sample_query.pyimport sqlite3
import timedb_conn = sqlite3.connect("people_db.db")
db_cursor = db_conn.cursor()t1 = time.perf_counter_ns()db_cursor.execute("SELECT name, email FROM people WHERE job='Product manager' LIMIT 10;")res = db_cursor.fetchall()
t2 = time.perf_counter_ns()print(res)
print(f"Query time with index: {(t2-t1)/1000} us")

以下是输出结果:

Output >>
[("Tina Woods", "Tina.Woods@smith.com"),("Toni Jackson", "Toni.Jackson@underwood.com"),("Lisa Miller", "Lisa.Miller@solis-west.info"),("Katherine Guerrero", "Katherine.Guerrero@schmidt-price.org"),("Michelle Lane", "Michelle.Lane@carr-hardy.com"),("Jane Johnson", "Jane.Johnson@graham.com"),("Matthew Odom", "Matthew.Odom@willis.biz"),("Isaac Daniel", "Isaac.Daniel@peck.com"),("Jay Byrd", "Jay.Byrd@bailey.info"),("Thomas Kirby", "Thomas.Kirby@west.com"),
]Query time with index: 167.179 us

可以看到查询现在大约需要167.179微秒来执行。

对于本文的示例查询,使用索引的查询速度大约快2.68倍,在执行时间方面获得了62.71%的速度提升。还可以尝试运行更多的查询:涉及筛选job列的查询,并查看性能的改进情况。

另请注意:由于只在job列上创建了索引,因此如果运行涉及其他列的查询,查询的运行速度不会比没有索引时更快。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/56097.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【javaweb】学习日记Day6 - Mysql 数据库 DDL DML

之前学习过的SQL语句笔记总结戳这里→【数据库原理与应用 - 第六章】T-SQL 在SQL Server的使用_Roye_ack的博客-CSDN博客 目录 一、概述 1、如何安装及配置路径Mysql? 2、SQL分类 二、DDL 数据定义 1、数据库操作 2、IDEA内置数据库使用 (1&…

Python3 列表

Python3 列表 序列是 Python 中最基本的数据结构。 序列中的每个值都有对应的位置值,称之为索引,第一个索引是 0,第二个索引是 1,依此类推。 Python 有 6 个序列的内置类型,但最常见的是列表和元组。 列表都可以进…

飞腾uboot命令简单介绍

飞腾uboot和开源uboot并无大差异,故飞腾uboot固件命令可以直接从网上搜索开源uboot相关命令。 这里为了便于大家调试,将一些可能用到的命令说明一下。 在 Uboot 命令行下,输入 help 将打印所有的可用命令,复杂命令操作,通过命令 help 的方式获取具体说明。 1.help命令 …

时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价)

时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价) 目录 时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价)效果一览基本描述程序设计参考资料 效果一览 基本描述 MATLAB实现DBN-SVM深度置信网络结合支持向量机…

校招算法题实在不会做,有没有关系?

文章目录 前言一、校招二、时间复杂度1、单层循环2、双层循环 三、空间复杂度四、数据结构五、校招算法题实在不会做,有没有关系?六、英雄算法集训 前言 英雄算法联盟八月集训 已经接近尾声,九月算法集训将于 09月01日 正式开始,目…

.NET 8 Preview 7 中的 ASP.NET Core 更新

作者:Daniel Roth 排版:Alan Wang .NET 8 Preview 7 现在已经发布,其中包括了对 ASP.NET Core 的许多重要更新。 以下是预览版本中新增功能的摘要: 服务器和中间件 防伪中间件 API 编写 最小 API 的防伪集成 Native AOT 请求委托…

市值暴跌后,每日优鲜能否靠2亿融资“续命”?

濒临破产退市的每日优鲜,靠转型实现“自救”? 作为“生鲜电商第一股”,每日优鲜在上市1年后爆发生存危机。 8月4日,每日优鲜(NDAQ:MF)公布了2022年报,尽管去年7月其宣布关闭营收占比约90%的DWM业务,全面终…

Kafka 消费者“group_name”组正在永远重新平衡

目录 一、场景1.1、场景应用环境1.2、 问题重现二、问题分析三、解决方案一、场景 1.1、场景应用环境 卡夫卡:2.11-1.0.1。主题:并发度为 5 且分区为 5 。1.2、 问题重现 当应用程序重新启动并且在分区分配之前在主题上发布消息时,主题的 5 个消费者找到组协调器并向组协调…

如何对MySQL和MariaDB中的查询和表进行优化-提升查询效率

前言 MySQL和MariaDB是数据库管理系统的流行选择。两者都使用SQL查询语言来输入和查询数据。 尽管SQL查询是简单易学的命令,但并不是所有的查询和数据库函数都具有相同的效率。随着你存储的信息量的增长,如果你的数据库支持一个网站,随着网…

【80天学习完《深入理解计算机系统》】第十一天 3.5 过程(函数调用)

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录)   文章字体风格: 红色文字表示&#…

RK3568 安卓源码编译

一.repo安卓编译工具 项目模块化/组件化之后各模块也作为独立的 Git 仓库从主项目里剥离了出去,各模块各自管理自己的版本。Android源码引用了很多开源项目,每一个子项目都是一个Git仓库,每个Git仓库都有很多分支版本,为了方便统…

gradio使用transformer模块demo介绍2:Images Computer Vision

文章目录 图像分类 Image Classification图像分割 Image Segmentation图像风格变换 Image Transformation with AnimeGAN3D模型 3D models 图像分类 Image Classification import gradio as gr import torch import requests from torchvision import transformsmodel torch.…

【Unity3D赛车游戏】【六】如何在Unity中为汽车添加发动机和手动挡变速?

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:Uni…

【【STM32分析IO该设置什么模式的问题】】

STM32分析IO该设置什么模式的问题 我们分析而言 我们对于PA0 的设计就从此而来 对于边沿触发的选择我们已经有所了解了 我们下拉,但是当我们摁下开关的时候 从0到1 导通了 所以这个是下拉 上升沿触发 而对于KEY0 我们摁下是使得电路从原来悬空高阻态到地就是0 所以…

龙芯2K1000LA移植交叉编译环境以及QT

嵌入式大赛结束了,根据这次比赛中记的凌乱的笔记,整理了一份龙芯2K1000LA的环境搭建过程,可能笔记缺少了一部分步骤或者错误,但是大致步骤可以当作参考。 一、交叉编译工具链 下载连接:龙芯 GNU 编译工具链 | 龙芯开…

几个nlp的小项目(文本分类)

几个nlp的小项目(文本分类) 导入加载数据类、评测类查看数据集精确展示数据测评方法设置参数tokenizer,token化的解释对数据集进行预处理加载预训练模型进行训练设置训练模型的参数一个根据任务名获取,测评方法的函数创建预训练模型开始训练本项目的工作完成了什么任务?导…

Flask 单元测试

如果一个软件项目没有经过测试,就像做的菜里没加盐一样。Flask 作为一个 Web 软件项目,如何做单元测试呢,今天我们来了解下,基于 unittest 的 Flask 项目的单元测试。 什么是单元测试 单元测试是软件测试的一种类型。顾名思义&a…

extern “C”关键字的作用

目录 概述C和C在函数调用和变量命名等方面的差异示例总结 概述 extern "C"是用于在C中声明使用C语言编写的函数和变量的关键字。C和C在函数调用和变量命名等方面存在一些差异,为了在C代码中正确地使用C语言的函数和变量,需要使用extern "…

Kubernetes-CKA考题详解

Kubernetes-CKA考题详解 考前须知:考试环境说明第一题:RBAC(4%)第二题:指定node设置为不可用(4%)第三题:升级kubernetes节点(7%)第四题:etcd备份还原(7%)第五题:创建NetworkPolicy(7%)第六题:创建svc(7%)第七题:创建ingress资源(7%)第八题:扩展deployme…

redis--集群

redis集群 Redis 集群是一种用于分布式存储和管理数据的解决方案,它允许将多个 Redis 实例组合成一个单一的逻辑数据库,提供更高的性能、容量和可用性。 redis集群的优点 高可用性: Redis集群使用主从复制和分片技术,使得数据可…