Как расширить это решение до соединения? При использовании у SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;меня всегда получается один и тот же ряд.
Helmut Grohne
Можно ли засеять случайное число. например, «Книга дня» заполнена unix epoc на сегодняшний день в полдень, поэтому она показывает одну и ту же книгу весь день, даже если запрос выполняется несколько раз. Да, я знаю, что кеширование более эффективно для этого варианта использования, просто для примера.
Следующие ниже решения намного быстрее, чем у 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, и выбор вернет пустой набор результатов. Если это неприемлемо, вы можете указать значение по умолчанию, подобное этому:
Это второе решение не идеально: распределение вероятностей выше в последней строке (той, у которой самый высокий rowid), но если вы часто добавляете что-то в таблицу, она станет движущейся целью, и распределение вероятностей должно быть намного лучше.
Еще одно решение: если вы часто выбираете случайный материал из таблицы с большим количеством дыр, вы можете создать таблицу, содержащую строки исходной таблицы, отсортированные в случайном порядке:
createtable random_foo(foo_id);
Затем периодически заново заполняйте таблицу random_foo
deletefrom random_foo;insertinto random_foo select id from foo;
А чтобы выбрать случайный ряд, можно воспользоваться моим первым методом (здесь нет дырок). Конечно, у этого последнего метода есть некоторые проблемы с параллелизмом, но восстановление random_foo - это операция поддержки, которая вряд ли будет происходить очень часто.
Тем не менее, еще один способ, который я недавно нашел в списке рассылки , - это установить триггер на удаление, чтобы переместить строку с самым большим идентификатором строки в текущую удаленную строку, чтобы не осталось дыр.
Наконец, обратите внимание, что поведение rowid и автоинкремента целочисленного первичного ключа не идентично (с rowid, когда вставляется новая строка, выбирается max (rowid) +1, тогда как это самое высокое значение из когда-либо существовавших + 1 для первичный ключ), поэтому последнее решение не будет работать с автоинкрементом в random_foo, но другие методы будут.
Как я только что видел в списке рассылки, вместо резервного метода (метод 2) вы можете просто использовать rowid> = [random] вместо =, но на самом деле он медленнее, чем метод 2.
Сюзанна Дюперон,
3
Это отличный ответ; однако у него есть одна проблема. 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/…
dasl
19
Вам нужно указать в вашем запросе "order by RANDOM ()" .
Пример:
select*from quest orderby RANDOM();
Посмотрим полный пример
Создайте таблицу:
CREATETABLE quest (
id INTEGER PRIMARYKEY AUTOINCREMENT,
quest TEXT NOTNULL,
resp_id INTEGER NOTNULL);
Хотя ответы только на код не запрещены, пожалуйста, поймите, что это сообщество вопросов и ответов, а не краудсорсинговое, и что, как правило, если OP понимает, что код публикуется как ответ, он / она подошел бы с аналогичным решением самостоятельно, и вообще не отправил бы вопрос. Таким образом, предоставьте контекст для своего ответа и / или кода, объяснив, как и / или почему это работает.
XenoRo
2
Я предпочитаю это решение, поскольку оно позволяет мне искать n строк. В моем случае мне понадобилось 100 случайных выборок из базы данных - ORDER BY RANDOM () в сочетании с LIMIT 100 делает именно это.
пн
17
Что о:
SELECT COUNT(*)AS n FROM foo;
затем выберите случайное число m в [0, n) и
SELECT*FROM foo LIMIT 1 OFFSET m;
Вы даже можете где-нибудь сохранить первое число ( n ) и обновлять его только при изменении количества в базе данных. Таким образом, вам не нужно каждый раз выполнять SELECT COUNT.
Это хороший быстрый метод. Это не очень хорошо подходит для выбора более одной строки, но OP запросил только 1, так что я думаю, что это нормально.
Кен Уильямс
Любопытно отметить, что время, необходимое для поиска, OFFSETкажется, увеличивается в зависимости от размера смещения - строка 2 выполняется быстро, строка 2 миллиона занимает некоторое время, даже если все данные в файле имеют фиксированный размер и должен иметь возможность искать прямо к нему. По крайней мере, так это выглядит в SQLite 3.7.13.
Кен Уильямс
@KenWilliams Практически все базы данных имеют одну и ту же проблему с OFFSET. Это очень неэффективный способ запроса к базе данных, потому что он должен прочитать такое количество строк, даже если он вернет только 1.
Джонатан Аллен,
1
Обратите внимание, что я говорил о / фиксированном размере / записях - должно быть легко сканировать непосредственно до правильного байта в данных ( не считывая так много строк), но им придется реализовать оптимизацию явно.
Кен Уильямс
@KenWilliams: в SQLite нет записей фиксированного размера, он динамически типизируется, и данные не обязательно должны соответствовать заявленному сходству ( sqlite.org/fileformat2.html#section_2_1 ). Все хранится на страницах b-дерева, так что в любом случае он должен выполнять хотя бы поиск b-дерева в направлении листа. Чтобы добиться этого эффективно, необходимо сохранить размер поддерева вместе с каждым дочерним указателем. Это будет слишком много накладных расходов и мало пользы, так как вы все равно не сможете оптимизировать OFFSET для объединений, упорядочивания и т. Д. (И без ORDER BY порядок не определен.)
Это решение также работает для индексов с пробелами, потому что мы рандомизируем смещение в диапазоне [0, count). MAXиспользуется для обработки случая с пустой таблицей.
Вот простые результаты теста на таблице с 16k строками:
sqlite>.timer on
sqlite>select count(*)from payment;16049
Run Time: real 0.000user0.000140 sys 0.000117
sqlite>select payment_id from payment limit 1 offset abs(random())%(select count(*)from payment);14746
Run Time: real 0.002user0.000899 sys 0.000132
sqlite>select payment_id from payment limit 1 offset abs(random())%(select count(*)from payment);12486
Run Time: real 0.001user0.000952 sys 0.000103
sqlite>select payment_id from payment orderby random() limit 1;3134
Run Time: real 0.015user0.014022 sys 0.000309
sqlite>select payment_id from payment orderby random() limit 1;9407
Run Time: real 0.018user0.013757 sys 0.000208
Хорошая попытка, но я не думаю, что это сработает. Что, если строка с rowId = 5 была удалена, но rowIds 1,2,3,4,6,7,8,9,10 все еще существует? Затем, если выбран случайный идентификатор строки 5, этот запрос ничего не вернет.
Ответы:
Взгляните на выбор случайной строки из таблицы SQLite
источник
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
дает более высокую производительность, как описано в комментариях):Если у вас есть дыры, вы иногда будете пытаться выбрать несуществующий rowid, и выбор вернет пустой набор результатов. Если это неприемлемо, вы можете указать значение по умолчанию, подобное этому:
Это второе решение не идеально: распределение вероятностей выше в последней строке (той, у которой самый высокий rowid), но если вы часто добавляете что-то в таблицу, она станет движущейся целью, и распределение вероятностей должно быть намного лучше.
Еще одно решение: если вы часто выбираете случайный материал из таблицы с большим количеством дыр, вы можете создать таблицу, содержащую строки исходной таблицы, отсортированные в случайном порядке:
Затем периодически заново заполняйте таблицу random_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 ()" .
Пример:
Посмотрим полный пример
Вставка некоторых значений:
Выбор по умолчанию:
Случайный выбор:
* Каждый раз, когда вы выбираете, порядок будет другим.Если вы хотите вернуть только одну строку
* Каждый раз, когда вы выбираете, возврат будет другим.источник
Что о:
затем выберите случайное число m в [0, n) и
Вы даже можете где-нибудь сохранить первое число ( n ) и обновлять его только при изменении количества в базе данных. Таким образом, вам не нужно каждый раз выполнять SELECT COUNT.
источник
OFFSET
кажется, увеличивается в зависимости от размера смещения - строка 2 выполняется быстро, строка 2 миллиона занимает некоторое время, даже если все данные в файле имеют фиксированный размер и должен иметь возможность искать прямо к нему. По крайней мере, так это выглядит в SQLite 3.7.13.источник
Вот модификация решения @ank:
Это решение также работает для индексов с пробелами, потому что мы рандомизируем смещение в диапазоне [0, count).
MAX
используется для обработки случая с пустой таблицей.Вот простые результаты теста на таблице с 16k строками:
источник
Я предложил следующее решение для больших баз данных sqlite3 :
Наконец, вы добавляете +1, чтобы rowid не был равен 0.
источник