Sqlite查询优化(使用限制和偏移)

lymnna71  于 2023-10-23  发布在  SQLite
关注(0)|答案(6)|浏览(162)

下面是我用来从一个有数百万条记录的数据库中获取固定数量的记录的查询:

select * from myTable LIMIT 100 OFFSET 0

我观察到的是,如果偏移量非常高,比如说90000,那么执行查询需要更多的时间。以下是具有不同偏移量的两个查询之间的时间差:

select * from myTable LIMIT 100 OFFSET 0       //Execution Time is less than 1sec
select * from myTable LIMIT 100 OFFSET 95000   //Execution Time is almost 15secs

有人能建议我如何优化这个查询吗?我的意思是,对于我希望从任何OFFSET检索的任何数量的记录,查询执行时间都应该是相同的,而且速度很快。
新添加的:-实际情况是,我有一个数据库有超过100万条记录。但由于它是一个嵌入式设备,我不能执行“select * from myTable”,然后从查询中获取所有记录。我的设备崩溃了。相反,我所做的是按照上面提到的查询逐批获取记录(批大小= 100或1000条记录)。但正如我提到的,它会随着偏移量的增加而变慢。所以,我的最终目标是从数据库中读取所有记录。但是,由于我不能在一次执行中获取所有记录,我需要一些其他有效的方法来实现这一点。

wwtsj6pe

wwtsj6pe1#

正如JvdBerg所说,LIMIT/OFFSET中不使用索引。简单地添加“ORDER BY indexed_field”也没有帮助。
为了加快分页速度,你应该避免LIMIT/OFFSET,而使用WHERE子句。例如,如果你的主键字段名为“id”,并且没有空格,那么上面的代码可以这样重写:

SELECT * FROM myTable WHERE id>=0     AND id<100     //very fast!
SELECT * FROM myTable WHERE id>=95000 AND id<95100   //as fast as previous line!
c0vxltue

c0vxltue2#

正如@user318750所说,如果您知道您有一个连续的索引,您可以简单地使用

select * from Table where index >= %start and index < %(start+size)

然而,这种情况很少见。如果你不想依赖这个假设,可以使用子查询,例如使用rowid,它总是被索引的,

select * from Table where rowid in (
  select rowid from Table limit %size offset %start)

这加快了速度,特别是如果你有“胖”行(例如。包含斑点)。
如果维护记录顺序很重要(通常不重要),则需要首先对索引进行排序:

select * from Table where rowid in (
  select rowid from Table order by rowid limit %size offset %start)
6ju8rftf

6ju8rftf3#

通过执行偏移量为95000的查询,将处理所有以前的95000条记录。你应该在表上做一些索引,并使用它来选择记录。

jv4diomz

jv4diomz4#

select * from data where rowid = (select rowid from data limit 1 offset 999999);
crcmnpdw

crcmnpdw5#

使用SQLite,你不需要在一个大的胖数组中一次返回所有的行,你可以为每一行回调。通过这种方式,您可以在结果出现时对其进行处理,这应该可以解决崩溃和性能问题。
我猜你没有使用C,因为你已经使用了回调,但这种技术应该在任何其他语言中都可用。
JavaScript示例(来自:https://www.npmjs.com/package/sqlite3

db.each("SELECT rowid AS id, info FROM lorem", function(err, row) {
      console.log(row.id + ": " + row.info);
  });
gopyfrb3

gopyfrb36#

有两种情况下的偏移,限制问题(例如,myOffset、myLimit)

1-偏移量可以基于数字、唯一和连续字段

如果这个字段是“id”,那么这两个过滤器中的任何一个都可以完成这项工作

... WHERE id >= myOffset LIMIT myLimit
  or
        ... WHERE id >= myOffset AND id < myOffset + myLimit

对于不包含显式数字唯一字段的表,rowid(由sqlite自动创建)是此类id的理想候选者
但即使在这种情况下,我们也可能在id中存在间隙,例如当记录被删除时。因此过滤器可能会返回重复的记录,或者返回数量少于myLimit的记录

2-偏移量不能直接基于表中的任何列

在这种情况下,除了使用OFFSET LIMIT并接受大偏移量的成本之外,没有其他方法。实际上sqlite无法优化这一点,因为它必须遍历所有以前的记录来计算它们。

或多或少静态表的解决方案:

如果所讨论的大表没有太多变化,或者我们可以在每次表变化时提供额外的查询时间

DROP TABLE tmpOffsetMap ;
  CREATE TABLE tmpOffsetMap AS SELECT rowid AS tableRowid FROM table ;

这将创建一个表,它的记录数与巨大的表“table”一样多,但只有两列:rowid和tableRowid
那么myOffset和myLimit在“table”上的选择将如下所示:

SELECT * FROM table WHERE rowid IN (SELECT tableRowid FROM tmpOffsetMap WHERE rowid >= myOffset AND rowid < myOffset + myLimit)

注意,“rowid”是不同的列,在表“table”和“tmpOffsetMap”中具有不同的值

相关问题