【问题标题】:Returning an OFFSET subquery result in sqlite3在 sqlite3 中返回一个 OFFSET 子查询结果
【发布时间】:2011-09-25 17:37:46
【问题描述】:

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

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 语法来请求该子查询的结果?

【问题讨论】:

    标签: sqlite subquery offset


    【解决方案1】:

    我编写了函数来保存我在原始帖子末尾提到的计数,因此这是删除重复索引搜索的一个可能答案。我仍然想知道这是否可以直接使用 SQL。

    我创建了一个直通用户函数来从 子查询作为偏移量计算。

    所以代替原来的查询:

    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 相比)。

    【讨论】:

    • Python 异常处理的开销是多少?在未缓存的情况下,执行cache.has_key(key)(或只是cache.get(key) 并检查None)是否明显更快?
    • 好问题!我刚刚在具有一百万个整数键的字典上用一百万个缓存命中和一百万个未命中测试了这一点。我尝试了四个选项:try/excepthas_keycache.get(key)key in cachetry/except 在密钥存在时具有出色的性能,但在未命中时比下一个差 4xkey in cache 在我的使用中具有最好的平均性能。
    猜你喜欢
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多