Optimization Sqlite subselect比distinct+order by快得多

Optimization Sqlite subselect比distinct+order by快得多,optimization,sqlite,Optimization,Sqlite,下面两个产生相同输出的查询的运行时间截然不同,这让我感到困惑。查询在Sqlite 3.7.9上运行,在一个大约有450万行的表上运行,每个查询产生大约50行结果 以下是查询: % echo "SELECT DISTINCT acolumn FROM atable ORDER BY acolumn;" | time sqlite3 mydb sqlite3 mydb 8.87s user 15.06s system 99% cpu 23.980 total % echo "SELECT ac

下面两个产生相同输出的查询的运行时间截然不同,这让我感到困惑。查询在Sqlite 3.7.9上运行,在一个大约有450万行的表上运行,每个查询产生大约50行结果

以下是查询:

% echo "SELECT DISTINCT acolumn FROM atable ORDER BY acolumn;" | time sqlite3 mydb
sqlite3 mydb  8.87s user 15.06s system 99% cpu 23.980 total


% echo "SELECT acolumn FROM (SELECT DISTINCT acolumn FROM atable) ORDER BY acolumn;" | time sqlite3 options
sqlite3 mydb  1.15s user 0.10s system 98% cpu 1.267 total
这两个查询的性能不是应该更接近吗?我知道查询计划器可能以不同的顺序执行排序和区分操作,但如果是这样,它是否需要?或者它应该能够找出如何以最快的速度完成这项任务

编辑:根据请求,这里是每个查询的解释查询计划命令的输出

对于第一个查询:

0|0|0|SCAN TABLE atable (~1000000 rows)
0|0|0|USE TEMP B-TREE FOR DISTINCT
对于第二个子查询:

1|0|0|SCAN TABLE atable (~1000000 rows)
1|0|0|USE TEMP B-TREE FOR DISTINCT
0|0|0|SCAN SUBQUERY 1 (~1000000 rows)
0|0|0|USE TEMP B-TREE FOR ORDER BY

在大多数DBMS中,SQL语句被转换为表达式树,然后在表达式树中进行结构化。 然后,dbms使用启发式优化查询。主要的启发式方法之一是第46页。我想sqlite查询计划器也会这样做,因此执行时间会有所不同


由于子查询的结果比450万行小得多(约50行),因此在表达式树末尾进行排序的速度要快得多。简单选择并不是一个非常昂贵的过程,在大量结果上运行操作确实是如此。

我认为这一定是因为顺序操作和不同操作在被子选择分隔时必须更有效地实现,这实际上是alexdeloy所说的更简单的方式

这个实验还没有完成。请同时运行以下程序:

% echo "SELECT acolumn FROM (SELECT DISTINCT acolumn FROM atable ORDER BY acolumn) ;" | time sqlite3 mydb

告诉我这是否比其他两个平均花费的时间长,谢谢。

您的第一个查询首先将记录插入一个已排序的临时表中,然后通过遍历记录并仅返回与前一个不相同的记录来实现DISTINCT。 这可以在下面显示的解释输出中看到;DISTINCT实际上被转换为一个GROUP BY,其行为相同

理论上,您的第二个查询与第一个查询相同,但SQLite的查询优化器相当简单,无法证明此转换是安全的,如中所述。 因此,它是通过首先执行DISTINCT来实现的,只将任何非重复项插入到临时表中,然后使用第二个临时表执行ORDER by。 第二步是完全多余的,因为第一个临时表已经被排序,但是对于您的数据来说,这恰好更快,因为您有太多的副本从未存储在任何一个临时表中

理论上,您的第一个查询可能会更快,因为SQLite已经认识到DISTINCT和ORDER BY子句可以用相同的排序临时表实现。 然而,在实践中,SQLite并没有足够聪明地记住DISTINCT意味着重复项不需要存储在temp表中。 如果您在页面上仔细询问,这个特定的优化可能会添加到SQLite中

$sqlite3 mydb sqlite>.explain sqlite>explain通过acolumn从表顺序中选择不同的acolumn; 地址操作码p1 p2 p3 p4 p5注释 -- ------- --- ------- - ------- 0跟踪0 0 00 1分拣机1 2 0钥匙信息1,二进制00 2整数0 3 0 00清除中止标志 3整数0 2 0 00表示累加器为空 4零0 6 00 5 Gosub 5 37 0 00 6转到0 40 00 7 OpenRead 0 2 0 1 00 atable 8倒带0 14 0 00 9列0 0 8 00 atable.a列 10序列19 00 11创造纪录8 2 10 00 12.1.10.00 13下一个0 9 0 01 14收盘0 00 15.2.10.2 00 16分拣机分拣1 39 0 00按分拣分组 17分类数据11000 18第2 0 7 20列 19比较6 7 1键信息1,二进制00 20跳21252100 21移动7 600 22 Gosub 4 32 0 00输出一行 23 IfPos 3 39 0 00支票 中止标志 24 Gosub 5 37 0 00复位蓄能器 25第2 0 1 00列 26整数1 2 0 00表示累加器中的数据 27分拣机下1 170 00 28 Gosub 4 32 0 00输出最后一行 29转到0 39 0 00 30整数1 3 0 00设置中止标志 31返回400 00 32 IfPos 2 34 0 00 Groupby结果生成器入口点 33返回400 00 34副本11100 35结果11 100 36返回4 0 0 00结束分组按结果生成器 37零0 1 0 00 38返回500 39暂停0 0 00 40事务处理0 00 41验证Cookie 0 2 0 00 42表锁0 2 0或表00 43转到0700 sqlite>explain SELECT acolumn FROM SELECT DISTINCT acolumn FROM atable ORDER BY acolumn; 地址操作码p1 p2 p3 p4 p5注释 -- ------- --- ------- - ------- 0跟踪0 0 00 1转到03900 2转到0 17 0 00 3用于sqlite_子查询_DA7480的OpenPseudo 0 3 1 01协同程序_ 4整数0 2 0 01 5 OpenEphemeral 2 0 0 keyinfo1,二进制08 6 OpenRead 1 2 0 1 00 atable 7倒带11400 8第1 0 3 00列atable.A列 9发现2 13 3 1 00 10创造纪录311400 11 IdxInsert 2 4 0 00 12收益率100 13下一个18 0 01 14收盘价100 15整数120 00 16产生1 0 0 00端sqlite_子查询_DA7480_ 17分拣机3 0钥匙信息1,二进制00 18整数21100 19生成共同例程sqlite_子查询_DA7480的下一行1 0 00_ 20如果2 29 00 21列0 0 5 00 sqlite\u子查询\u DA7480\u.a列 22创纪录51600 23列0 0 7 00 sqlite\u子查询\u DA7480\u.a列 24序列38000 25移动6900 26创纪录7 3 10 00 27.3.10.00 28转0 19 0 00 29.4.6.1 00 30:5 11 3 00 31分拣口3 37 0 00 32分类数据31100 33第5列2 6 20 34第4 0 5 20列 35结果5 1 0 00 36分拣机下3 32 0 00 37结束400 38暂停0 0 00 39事务处理0 00 40验证Cookie 0 2 0 00 41表锁0 2 0表00 42转到02 00
请显示两个查询的输出。intresting,是否运行了多个查询
时代?以不同的顺序?我想重复你的实验,你能分享db吗?你使用什么平台?我可以用最新的SQLite在多个平台上复制它。愚蠢的测试数据:是的,压缩了两次。与上面的第一次查询花费的时间大致相同。我希望计划看起来也一样。如果是这样的话,那么我想这就支持了这样一种理论,即在临时b-树上执行distinct操作,然后在最终的b-树上执行排序,在sqllite中效率更高。要真正确定,可能需要调试程序。