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

时间:2016-04-02 16:27:09

标签: sql sqlite

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

SELECT fileno, name FROM dirents WHERE dirno = ? LIMIT -1 OFFSET ?;

其中dirno是我正在读取的目录,OFFSET ?是我已经返回的条目数。我想读取尽可能多的行,因为我可以放入缓冲区(我无法预测计数,因为这些是可变长度记录,具体取决于文件名的长度),然后重置查询。

由于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语句并且连续更大的OFFSET值时,我会得到相同的结果,就像我在单个查询中读取所有数据一样,即行顺序每次都是相同的未指定顺序?当前持有的假设是在查询之间不修改数据库。

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

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

我知道我可以使用ORDER BY子句来使行顺序定义明确,但我担心这可能会对性能产生很大影响,尤其是在读取大型目录的小块时 - 每次读取一个块时都必须对目录进行排序。

2 个答案:

答案 0 :(得分:2)

此问题的正确解决方案是使用order by。如果您担心订单的性能,请使用用于order by的列的索引。

在我看来,最简单的方法是删除表创建中的without rowid选项。然后您只需访问该表:

SELECT fileno, name
FROM dirents
WHERE dirno = ?
ORDER BY rowid
LIMIT -1 OFFSET ?;

我意识到这会为每一行添加额外的字节,但它有一个很好的用途 - 确保您的查询正确。

实际上,此表的最佳索引是dirno, rowid, fileno, name。给定where子句,无论如何,您都要进行全表扫描,除非您有索引。

答案 1 :(得分:0)

如果我向ORDER BY name查询添加SELECT子句,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   

所以我想我会选择ORDER BY name条款。