У меня есть sqlite
таблица со следующей схемой:
CREATE TABLE foo (bar VARCHAR)
Я использую эту таблицу как хранилище для списка строк.
Как выбрать случайную строку из этой таблицы?
У меня есть sqlite
таблица со следующей схемой:
CREATE TABLE foo (bar VARCHAR)
Я использую эту таблицу как хранилище для списка строк.
Как выбрать случайную строку из этой таблицы?
Ответы:
Взгляните на выбор случайной строки из таблицы SQLite
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;
меня всегда получается один и тот же ряд.
Следующие ниже решения намного быстрее, чем у anktastic (счетчик (*) стоит дорого, но если вы можете его кэшировать, то разница не должна быть такой большой), что само по себе намного быстрее, чем «порядок случайным ()» когда у вас большое количество рядов, хотя в них есть несколько неудобств.
Если ваши идентификаторы строк довольно упакованы (т. Е. Несколько удалений), вы можете сделать следующее (использование (select max(rowid) from foo)+1
вместо max(rowid)+1
дает более высокую производительность, как описано в комментариях):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Если у вас есть дыры, вы иногда будете пытаться выбрать несуществующий rowid, и выбор вернет пустой набор результатов. Если это неприемлемо, вы можете указать значение по умолчанию, подобное этому:
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)) or rowid = (select max(rowid) from node) order by rowid limit 1;
Это второе решение не идеально: распределение вероятностей выше в последней строке (той, у которой самый высокий rowid), но если вы часто добавляете что-то в таблицу, она станет движущейся целью, и распределение вероятностей должно быть намного лучше.
Еще одно решение: если вы часто выбираете случайный материал из таблицы с большим количеством дыр, вы можете создать таблицу, содержащую строки исходной таблицы, отсортированные в случайном порядке:
create table random_foo(foo_id);
Затем периодически заново заполняйте таблицу random_foo
delete from random_foo;
insert into random_foo select id from foo;
А чтобы выбрать случайный ряд, можно воспользоваться моим первым методом (здесь нет дырок). Конечно, у этого последнего метода есть некоторые проблемы с параллелизмом, но восстановление random_foo - это операция поддержки, которая вряд ли будет происходить очень часто.
Тем не менее, еще один способ, который я недавно нашел в списке рассылки , - это установить триггер на удаление, чтобы переместить строку с самым большим идентификатором строки в текущую удаленную строку, чтобы не осталось дыр.
Наконец, обратите внимание, что поведение rowid и автоинкремента целочисленного первичного ключа не идентично (с rowid, когда вставляется новая строка, выбирается max (rowid) +1, тогда как это самое высокое значение из когда-либо существовавших + 1 для первичный ключ), поэтому последнее решение не будет работать с автоинкрементом в random_foo, но другие методы будут.
SELECT max(rowid) + 1
будет медленным запросом - он требует полного сканирования таблицы. sqlite только оптимизирует запрос SELECT max(rowid)
. Таким образом, этот ответ может быть улучшен следующим образом: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
См. Это для получения дополнительной информации: sqlite.1065341.n5.nabble.com/…
Вам нужно указать в вашем запросе "order by RANDOM ()" .
Пример:
select * from quest order by RANDOM();
Посмотрим полный пример
CREATE TABLE quest (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quest TEXT NOT NULL,
resp_id INTEGER NOT NULL
);
Вставка некоторых значений:
insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);
Выбор по умолчанию:
select * from quest;
| id | quest | resp_id |
1 1024/4 6
2 256/2 12
3 128/1 24
--
Случайный выбор:
select * from quest order by RANDOM();
| id | quest | resp_id |
3 128/1 24
1 1024/4 6
2 256/2 12
--
* Каждый раз, когда вы выбираете, порядок будет другим.
Если вы хотите вернуть только одну строку
select * from quest order by RANDOM() LIMIT 1;
| id | quest | resp_id |
2 256/2 12
--
* Каждый раз, когда вы выбираете, возврат будет другим.
Что о:
SELECT COUNT(*) AS n FROM foo;
затем выберите случайное число m в [0, n) и
SELECT * FROM foo LIMIT 1 OFFSET m;
Вы даже можете где-нибудь сохранить первое число ( n ) и обновлять его только при изменении количества в базе данных. Таким образом, вам не нужно каждый раз выполнять SELECT COUNT.
OFFSET
кажется, увеличивается в зависимости от размера смещения - строка 2 выполняется быстро, строка 2 миллиона занимает некоторое время, даже если все данные в файле имеют фиксированный размер и должен иметь возможность искать прямо к нему. По крайней мере, так это выглядит в SQLite 3.7.13.
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
Вот модификация решения @ank:
SELECT *
FROM table
LIMIT 1
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)
Это решение также работает для индексов с пробелами, потому что мы рандомизируем смещение в диапазоне [0, count). MAX
используется для обработки случая с пустой таблицей.
Вот простые результаты теста на таблице с 16k строками:
sqlite> .timer on
sqlite> select count(*) from payment;
16049
Run Time: real 0.000 user 0.000140 sys 0.000117
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
14746
Run Time: real 0.002 user 0.000899 sys 0.000132
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
12486
Run Time: real 0.001 user 0.000952 sys 0.000103
sqlite> select payment_id from payment order by random() limit 1;
3134
Run Time: real 0.015 user 0.014022 sys 0.000309
sqlite> select payment_id from payment order by random() limit 1;
9407
Run Time: real 0.018 user 0.013757 sys 0.000208
Я предложил следующее решение для больших баз данных sqlite3 :
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
Функция abs (X) возвращает абсолютное значение числового аргумента X.
Функция random () возвращает псевдослучайное целое число от -9223372036854775808 до +9223372036854775807.
Оператор% выводит целочисленное значение своего левого операнда по модулю правого операнда.
Наконец, вы добавляете +1, чтобы rowid не был равен 0.