2011-09-25 40 views
3

我通過使用子查詢,以確定隨機偏移值選擇在SQLite的表中的隨機行:返回偏移子查詢結果中的sqlite3

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轉儲的查詢,該值只是遙不可及。這是一個在寄存器(在步驟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

我寫了一個函數來保存我在原始文章末尾提到的計數,所以這裏有一個可能的解決方法來刪除重複的索引搜索。我仍然想知道這是否適用於直接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 = ?)); 

的第一個參數緩存()是該數的唯一標識符。 I 可以只使用prev_node的值,但由於應用程序I 需要能夠分別緩存前向和後向步行的計數 。所以我使用「$方向:$ 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) 。

+1

什麼是在Python異常處理的開銷?在未緩存的情況下執行'cache.has_key(key)'(或只是'cache.get(key)'並檢查'None')明顯更快嗎? –

+1

好問題!我用一百萬個整數密鑰在字典中測試了一百萬次緩存命中和一百萬次未命中。我嘗試了四種選擇:'try/except','has_key','cache。獲取(鍵)'和'鍵入緩存'。當密鑰存在時,「try/except」的性能很好,但比未來的密鑰更糟糕_4x_。 '緩存中的密鑰'具有我使用的最佳平均性能。 –