Возврат результата подзапроса OFFSET в sqlite3 - PullRequest
3 голосов
/ 25 сентября 2011

Я выбираю случайную строку из таблицы в 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 для запроса результата этого подзапроса?

1 Ответ

1 голос
/ 26 сентября 2011

Я написал функцию для сохранения счетчиков, о которых я упоминал, в конце оригинального сообщения, так что вот один из возможных ответов для удаления поиска по дублирующему индексу.Я все еще хотел бы знать, выполнимо ли это с прямым SQL.

Я создал сквозную пользовательскую функцию для захвата счетчика из подзапроса при вычислении смещения.

Так что вместо оригиналаquery:

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,7 и 10,9)..

...