Выберите случайную строку из таблицы sqlite

78

У меня есть таблица sqlite со следующей схемой:

CREATE TABLE foo (bar VARCHAR)

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

Как выбрать случайную строку из этой таблицы?

Теги:
random
row

7 ответов

162

Посмотрите Выбор случайной строки из таблицы SQLite

SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
  • 1
    Ваш ответ, похоже, похож на ответ, предоставленный Светлозаром. Так что у меня к тебе такой же вопрос.
  • 1
    Как распространить это решение на объединение? При использовании SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1; Я всегда получаю один и тот же ряд.
25

Следующие решения намного быстрее, чем anktastic (счет (*) стоит много, но если вы можете кэшировать его, то разница не должна быть такой большой), что само намного быстрее, чем "порядок случайным()", когда у вас большое количество строк, хотя у них есть несколько неудобств.

Если ваши rowid довольно упакованы (т.е. несколько удалений), то вы можете сделать следующее (использование (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 вернет пустой набор результатов. Если это неприемлемо, вы можете указать значение по умолчанию, подобное этому:

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;

Это второе решение не является совершенным: распределение вероятности выше в последней строке (той, которая имеет самый высокий уровень), но если вы часто добавляете материал в таблицу, она станет движущейся целью и распределением вероятности должны быть намного лучше.

Еще одно решение, если вы часто выбираете случайные вещи из таблицы с большим количеством отверстий, тогда вам может понадобиться создать таблицу, содержащую строки исходной таблицы, отсортированные в произвольном порядке:

create table random_foo(foo_id);

Затем, периодически, снова заполните таблицу random_foo

delete from random_foo;
insert into random_foo select id from foo;

И чтобы выбрать случайную строку, вы можете использовать мой первый метод (здесь нет отверстий). Конечно, этот последний метод имеет некоторые проблемы concurrency, но перестройка random_foo - это операция поддержки, которая вряд ли произойдет очень часто.

Тем не менее, еще один способ, который я недавно нашел в список рассылки, - это поставить триггер для удаления, чтобы переместить строку с помощью самый большой rowid в текущей удаленной строке, так что никаких дыр не осталось.

Наконец, обратите внимание, что поведение автоинкремента rowid и целочисленного первичного ключа не идентично (с помощью rowid, когда вставлена ​​новая строка, выбирается max (rowid) +1, если это самый высокий показатель за всю историю +1 для первичного ключа), поэтому последнее решение не будет работать с автоинкрементами в random_foo, но другие методы будут.

  • 0
    Как я только что видел в списке рассылки, вместо использования резервного метода (метод 2), вы можете просто использовать rowid> = [random] вместо =, но на самом деле он медлительный по сравнению со способом 2.
  • 1
    Это отличный ответ; Однако это имеет одну проблему. 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/…
13

Как насчет:

SELECT COUNT(*) AS n FROM foo;

затем выберите случайное число m в [0, n) и

SELECT * FROM foo LIMIT 1 OFFSET m;

Вы даже можете сохранить первое число (n) и обновить его только при изменении значения базы данных. Таким образом, вам не нужно делать SELECT COUNT каждый раз.

  • 1
    Это хороший быстрый метод. Он не очень хорошо подходит для выбора более 1 строки, но ОП запрашивает только 1, так что я думаю, это нормально.
  • 0
    Любопытно отметить, что время, необходимое для нахождения OFFSET кажется, увеличивается в зависимости от размера смещения - строка 2 быстрая, строка 2 миллиона занимает некоторое время, даже когда все данные в ней имеют фиксированный размер и он должен быть в состоянии искать непосредственно к нему. По крайней мере, так выглядит SQLite 3.7.13.
Показать ещё 3 комментария
8
SELECT   bar
FROM     foo
ORDER BY Random()
LIMIT    1
  • 10
    Так как сначала будет выбран весь контент таблицы, не будет ли это занимать много времени для больших таблиц?
  • 1
    Разве вы не можете просто ограничить область, используя условия "ГДЕ"?
2

В запросе вам нужно указать "по 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
--
* Каждый раз, когда вы выбираете, возврат будет другим.
  • 0
    Хотя ответы только на код не запрещены, пожалуйста, поймите, что это сообщество вопросов и ответов, а не краудсорсинговое, и что, как правило, если бы ОП понимал код, публикуемый как ответ, он или она должен был прийти с аналогичным решением самостоятельно, и не стал бы публиковать вопрос в первую очередь. Поэтому , пожалуйста, предоставьте контекст для своего ответа и / или кода, объяснив, как и / или почему он работает.
  • 1
    Я предпочитаю это решение, так как оно позволяет мне искать n строк. В моем случае мне понадобилось 100 случайных выборок из базы данных - ORDER BY RANDOM () в сочетании с LIMIT 100 делает именно это.
2

Вот модификация решения @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
0

Я придумал следующее решение для больших баз данных sqlite3:

SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1; 

Функция abs (X) возвращает абсолютное значение числового аргумента X.

Функция random() возвращает псевдослучайное целое число между -9223372036854775808 и +9223372036854775807.

Оператор% выводит целочисленное значение своего левого операнда по модулю своего правого операнда.

Наконец, вы добавляете +1 для предотвращения того, чтобы rowid равнялся 0.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню