Sql 我可以使用LIMIT和OFFSET以迭代方式获取SELECT查询的结果吗?

Sql 我可以使用LIMIT和OFFSET以迭代方式获取SELECT查询的结果吗?,sql,sqlite,Sql,Sqlite,我正在实现一个FUSE文件系统,它的后端使用sqlite3数据库。我不打算更改数据库后端,因为我的文件系统使用sqlite3作为文件格式。文件系统必须实现的功能之一是readdir函数。此函数允许进程通过重复调用目录并获取缓冲区所能容纳的下几个目录项,以迭代方式读取目录的内容。返回的目录项可以按任何顺序返回。我希望通过以下查询实现此操作: SELECT fileno, name FROM dirents WHERE dirno = ? LIMIT -1 OFFSET ?; 其中dirno是我正

我正在实现一个FUSE文件系统,它的后端使用sqlite3数据库。我不打算更改数据库后端,因为我的文件系统使用sqlite3作为文件格式。文件系统必须实现的功能之一是readdir函数。此函数允许进程通过重复调用目录并获取缓冲区所能容纳的下几个目录项,以迭代方式读取目录的内容。返回的目录项可以按任何顺序返回。我希望通过以下查询实现此操作:

SELECT fileno, name FROM dirents WHERE dirno = ? LIMIT -1 OFFSET ?;
其中dirno是我正在读取的目录和偏移量?是我已返回的条目数。我想在缓冲区中读取尽可能多的行,但我无法预测计数,因为这些是可变长度的记录,取决于文件名的长度,然后重置查询

由于FUSE的无状态特性,保持打开查询并返回接下来的几行直到目录结束不是一个选项,因为我无法可靠地检测进程是否过早地关闭目录

dirents表具有以下架构:

CREATE TABLE dirents (
    dirno INTEGER NOT NULL REFERENCES inodes(inum),
    fileno INTEGER NOT NULL REFERENCES inodes(inum),
    name TEXT NOT NULL,
    PRIMARY KEY (dirno, name)
) WITHOUT ROWID;
问题 理论上,SELECT语句不按定义的顺序生成行。在实践中,我是否可以假设,当我以连续较大的偏移量值多次执行同一个准备好的SELECT语句时,我会得到相同的结果,就像我在单个查询中读取所有数据一样,即每次的行顺序都是相同的未指定顺序?目前的一个假设是,在查询之间不会修改数据库

当一个不同的查询修改中间的dirents表时,我可以假定行顺序保持合理的相似性吗?一些小故障(例如,目录条目出现两次)当然会被程序观察到,但对于可用性来说,readdir的主要用户是ls命令。如果目录列表通常都是正确的,那么它非常有用

如果我不能做出这些假设,那么达到预期结果的更好方法是什么


我知道我可以加入ORDER BY子句来定义行顺序,但我担心这可能会对性能产生严重影响,特别是在读取大目录的小块时,每次读取块时都必须对目录进行排序。

解决此问题的正确方法是使用ORDER BY。如果您担心order by的性能,请在order by使用的列上使用索引

在我看来,最简单的方法是删除表创建中的不带rowid选项。然后,您可以通过以下方式访问该表:

SELECT fileno, name
FROM dirents
WHERE dirno = ?
ORDER BY rowid
LIMIT -1 OFFSET ?;
我意识到这会给每一行增加额外的字节,但这是为了一个好的目的——确保您的查询是正确的

实际上,这个表的最佳索引应该是dirno、rowid、fileno、name。给定where子句,除非您有索引,否则您仍在执行完整的表扫描。

如果我在SELECT查询中添加ORDER BY name子句,sqlite3将生成几乎相同的查询,除了一个零散的Noop字节码,但可以保证行顺序:

sqlite> EXPLAIN SELECT fileno, name FROM dirents WHERE dirno = ? LIMIT -1 OFFSET ?;
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     21    0                    00               
1     Integer        -1    1     0                    00               
2     Variable       2     2     0                    00               
3     MustBeInt      2     0     0                    00               
4     SetIfNotPos    2     2     0                    00               
5     Add            1     2     3                    00               
6     SetIfNotPos    1     3     -1                   00               
7     OpenRead       1     3     0     k(2,nil,nil)   02               
8     Variable       1     4     0                    00               
9     IsNull         4     19    0                    00               
10    Affinity       4     1     0     D              00               
11    SeekGE         1     19    4     1              00               
12      IdxGT          1     19    4     1              00               
13      IfPos          2     18    1                    00               
14      Column         1     2     5                    00               
15      Column         1     1     6                    00               
16      ResultRow      5     2     0                    00               
17      DecrJumpZero   1     19    0                    00               
18    Next           1     12    0                    00               
19    Close          1     0     0                    00               
20    Halt           0     0     0                    00               
21    Transaction    0     0     3     0              01               
22    TableLock      0     3     0     dirents        00               
23    Goto           0     1     0                    00               
sqlite> EXPLAIN SELECT fileno, name FROM dirents WHERE dirno = ? ORDER BY name LIMIT -1 OFFSET ?;
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     22    0                    00               
1     Noop           0     0     0                    00               
2     Integer        -1    1     0                    00               
3     Variable       2     2     0                    00               
4     MustBeInt      2     0     0                    00               
5     SetIfNotPos    2     2     0                    00               
6     Add            1     2     3                    00               
7     SetIfNotPos    1     3     -1                   00               
8     OpenRead       2     3     0     k(2,nil,nil)   02               
9     Variable       1     4     0                    00               
10    IsNull         4     20    0                    00               
11    Affinity       4     1     0     D              00               
12    SeekGE         2     20    4     1              00               
13      IdxGT          2     20    4     1              00               
14      IfPos          2     19    1                    00               
15      Column         2     2     5                    00               
16      Column         2     1     6                    00               
17      ResultRow      5     2     0                    00               
18      DecrJumpZero   1     20    0                    00               
19    Next           2     13    0                    00               
20    Close          2     0     0                    00               
21    Halt           0     0     0                    00               
22    Transaction    0     0     3     0              01               
23    TableLock      0     3     0     dirents        00               
24    Goto           0     1     0                    00   

所以我想我将使用ORDERBY name子句。

只需在查询中添加一个ORDERBY,结果就稳定了。你为什么要在没有rowid的情况下创建它?这显然是获取数据的方法。@GordonLinoff看一下我问题的最后一段。@GordonLinoff我创建的表没有rowid,因此dirno,name是一个聚集索引。该表中最常见的查询是从dirents中选择fileno,其中dirno=?名称=?;。不,您不能假设没有ORDERBY子句的查询具有任何特定的顺序。但按索引对应的一组列排序应该不会太贵。没有ROWID的表需要有一个主键,因此需要有一个索引,这样就可以按主键排序。如果我为聚集索引保留没有ROWID的表,并按dirno,name排序,会不会一样好?@fuzzxl。当然可以。对于未来的读者:如果您按主键或主键的前几列排序,order by子句是免费的,因为在EXPLAIN显示的字节码中无法观察到order by less查询的差异。