在sqlite3中返回OFFSET子查询结果

时间:2011-09-25 17:37:47

标签: sqlite subquery offset

我通过使用子查询来确定随机OFFSET值,从SQLite中的表中选择一个随机行:

SELECT id, prev_node, next_node FROM edges WHERE prev_node = ? LIMIT 1
    OFFSET abs(random())%(SELECT count(*) FROM edges WHERE prev_node = ?);

这在我的任务中功能正确,但它需要对索引进行两次点击:

0|0|TABLE edges WITH INDEX edges_all_prev
0|0|TABLE edges WITH INDEX edges_all_prev

该查询用于随机游走,它很可能多次访问同一节点,因此随着边数的增加,缓存SELECT count(*)子查询的结果会很有帮助。

我可以选择该子查询的值以及其他返回的值吗?

查看查询的VDBE转储,该值是遥不可及的。它是在寄存器 8 (在步骤21中移动),而结果行是从寄存器 16-18 创建的(步骤42):

0|Trace|0|0|0||00|
1|Integer|1|1|0||00|
2|Function|0|0|5|random(0)|00|
3|Function|0|5|4|abs(1)|01|
4|If|7|23|0||00|
5|Integer|1|7|0||00|
6|Null|0|8|0||00|
7|Integer|1|9|0||00|
8|Null|0|10|0||00|
9|Variable|2|11|1||00|
10|Goto|0|47|0||00|
11|OpenRead|2|15|0|keyinfo(4,BINARY,BINARY)|00|
12|IsNull|11|18|0||00|
13|Affinity|11|1|0|d|00|
14|SeekGe|2|18|11|1|00|
15|IdxGE|2|18|11|1|01|
16|AggStep|0|0|10|count(0)|00|
17|Next|2|15|0||00|
18|Close|2|0|0||00|
19|AggFinal|10|0|0|count(0)|00|
20|SCopy|10|13|0||00|
21|Move|13|8|1||00|
22|IfZero|9|23|-1||00|
23|Remainder|8|4|2||00|
24|MustBeInt|2|0|0||00|
25|IfPos|2|27|0||00|
26|Integer|0|2|0||00|
33|Affinity|14|1|0|d|00|
34|SeekGe|3|45|14|1|00|
35|IdxGE|3|45|14|1|01|
36|AddImm|2|-1|0||00|
37|IfNeg|2|39|0||00|
38|Goto|0|44|0||00|
39|IdxRowid|3|16|0||00|
40|Column|3|0|17||00|
41|Column|3|1|18||00|
42|ResultRow|16|3|0||00|
43|IfZero|1|45|-1||00|
44|Next|3|35|0||00|
45|Close|3|0|0||00|
46|Halt|0|0|0||00|
47|Transaction|0|0|0||00|
48|VerifyCookie|0|27|0||00|
49|TableLock|0|9|0|edges|00|
50|Goto|0|11|0||00|

我可以创建一个在计算之后保存计数的函数,但是有一个简单的SQL语法来请求该子查询的结果吗?

1 个答案:

答案 0 :(得分:1)

我写了这个函数来保存我在原帖后面提到的计数,所以这里有一个可能的答案来删除重复的索引搜索。我仍然想知道这是否适用于直接SQL。

我创建了一个passthrough用户函数来捕获来自的计数 计算偏移量的子查询。

所以不是原始查询:

SELECT id, prev_node, next_node FROM edges WHERE prev_node = ? LIMIT 1
    OFFSET abs(random())%(
    SELECT count(*) FROM edges WHERE prev_node = ?);

我有更像这样的东西:

SELECT id, prev_node, next_node FROM edges WHERE next_node = ? LIMIT 1
    OFFSET abs(random())%(
    cache(?, (SELECT count(*) FROM edges WHERE prev_node = ?));

cache()的第一个参数是该计数的唯一标识符。一世 可以只使用prev_node的值,但由于应用程序我 需要能够缓存前进和后退步行的计数 分别。所以我使用“$ direction:$ prev_node_id”作为密钥。

缓存功能如下所示(使用Python):

_cache = {}
def _cache_count(self, key, count):
    self._cache[key] = count
    return count

conn.create_function("cache", 2, self._cache_count)

然后在随机游走功能中,我可以将哈希键和 检查计数是否已知。如果是,我使用的变体 不包含子查询的主查询:

uncached = "SELECT id, next_node, prev_node " \
    "FROM edges WHERE prev_node = :last LIMIT 1 " \
    "OFFSET abs(random())%cache(:key, " \
    "    (SELECT count(*) FROM edges WHERE prev_node = :last))"

cached = "SELECT id, next_node, prev_node, has_space, count " \
    "FROM edges WHERE prev_node = :last LIMIT 1 " \
    "OFFSET abs(random())%:count"

key = "%s:%s" % (direction, last_node)
if key in cache:
    count = cache[key]
    query = cached
    args = dict(last=last_node, count=count)
else:
    query = uncached
    args = dict(last=last_node, key=key)

row = c.execute(query, args).fetchone()

缓存查询的平均运行速度是未缓存的两倍(5.7us 与10.9us)。