Python 为什么SQLite中的多列索引会降低查询的性能,除非索引所有列?

Python 为什么SQLite中的多列索引会降低查询的性能,除非索引所有列?,python,sql,sqlite,indexing,covering-index,Python,Sql,Sqlite,Indexing,Covering Index,我试图通过使用索引优化对SQLite数据库的简单查询的性能。例如,该表有5行5列;SELECT语句将拾取所有列,WHERE语句仅检查2列。但是,除非我在多列索引中有所有列,否则查询的性能比没有任何索引时要差 我是否对列编制了错误的索引,或者在选择所有列时,是否应该将所有列都包含在索引中以提高性能 下面是我在硬盘中创建SQLite数据库时得到的结果。但是,由于某些原因,使用“:memory:”模式使所有索引案例比不使用索引的情况更快 导入sqlite3 导入日期时间 作为pd进口熊猫 将numpy

我试图通过使用索引优化对SQLite数据库的简单查询的性能。例如,该表有5行5列;SELECT语句将拾取所有列,WHERE语句仅检查2列。但是,除非我在多列索引中有所有列,否则查询的性能比没有任何索引时要差

我是否对列编制了错误的索引,或者在选择所有列时,是否应该将所有列都包含在索引中以提高性能

下面是我在硬盘中创建SQLite数据库时得到的结果。但是,由于某些原因,使用“:memory:”模式使所有索引案例比不使用索引的情况更快

导入sqlite3 导入日期时间 作为pd进口熊猫 将numpy作为np导入 导入操作系统 导入时间 模拟数据 尺寸=5000000 apps=[f'{i:010}'表示rangesize中的i] 日期=np.random.choicepd.date_range'2016-01-01','2019-01-01'。至_pydatetime.tolist,大小 prod_cd=np.random.choice[f'prod_{i}用于范围30中的i],大小 models=np.random.choice[f'MODEL{i}用于范围15中的i],大小 categories=np.random.choice[f'groupi{i}用于范围10中的i],大小 在内存中创建数据库 conn=sqlite3.connect':内存:',检测\类型=sqlite3.PARSE\数据类型 c=连接光标 创建表并插入数据 c、 已执行ROP表(如果存在) c、 executeCREATE表实验appId文本、数据输入时间戳、产品cd文本、模型文本、类别文本 c、 ExecuteMain插入实验值?、?、?、?、?、拉链、日期、产品cd、型号、类别 帮助函数 定义时间函数: def包装器*args,**kwargs: 开始=时间 结果=func*args,**kwargs {}函数的printtime为{}.formatfunc.\uuuuu name\uuuuuu,time.time-start 返回结果 返回包装器 @时间到了 def read_数据库查询: df=pd.read\u sql\u queryquery,conn 返回df @时间到了 def run_查询查询: 输出=c.executequery.fetchall 打印输出 主查询 query=从实验中选择*,其中prod_cd位于'prod_1'、'prod_5'、'prod_10'和dtenter>='2018-01-01' 案例1:没有任何索引 运行\u queryEXPLAIN查询计划+查询 df=读取数据查询 >>>read_db函数的时间为2.4783718585968018 案例2:在WHERE语句中使用列索引 如果存在idx,则运行\u queryrop索引 在experimentprod\u cd上运行\u queryCREATE INDEX idx,数据输入 运行\u queryEXPLAIN查询计划+查询 df=读取数据查询 >>>read_db函数的时间为3.221407890319824 案例3:索引超过WHERE语句中的内容,但不是所有列 如果存在idx,则运行\u queryrop索引 在experimentprod\u cd、dtenter、appId、category上运行\u queryCREATE INDEX idx 运行\u queryEXPLAIN查询计划+查询 df=读取数据查询 >>>read_db函数的时间为3.176532745361328 案例4:所有列都有索引 如果存在idx,则运行\u queryrop索引 在experimentprod\u cd、dtenter、appId、category、model上运行\u queryCREATE INDEX idx 运行\u queryEXPLAIN查询计划+查询 df=读取数据查询 >>>读取\u db函数的时间为0.8257918357849121 说:

对行执行索引查找时,通常的过程是对索引执行二进制搜索以查找索引项,然后从索引中提取rowid,并使用该rowid对原始表执行二进制搜索。因此,典型的索引查找涉及两个二进制搜索

索引项的顺序与表项的顺序不同,因此,如果查询从表的大多数页面返回数据,那么所有这些随机访问查找都比只扫描所有表行慢

只有当WHERE条件过滤出比返回的行多得多的行时,索引查找才比表扫描更有效

SQLite假定对索引列的查找具有高选择性。填写表格后运行,可以获得更好的估计。 但是,如果您所有的查询都是以索引没有帮助的形式进行的,那么最好不要使用索引

在查询中使用的所有列上创建索引时,不再需要额外的表访问:

但是,如果要从表中提取的所有列在索引本身中都已可用,SQLite将使用索引中包含的值,并且永远不会查找原始表行。这为每行节省了一个二进制搜索,并且可以使许多查询的运行速度提高一倍

当索引包含查询所需的所有数据,并且不需要查询原始表时,我们称该索引为覆盖索引


谢谢你的解释!现在情况变得更清楚了。但是:1。当您提到索引查找用于筛选出比返回的行多得多的行时效率更高,是否有近似的百分比?我上面的例子已经过滤掉了>95%的行。2.整个表有5百万行,所以我想使用Logn进行2次二进制搜索仍然会快得多
r比5米行的全扫描要多。我的理解正确吗?确切的阈值取决于数据、软件和硬件。进行表格扫描时,各个页面可能会连续存储在磁盘上。