У мене є 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;
я завжди отримую один і той же рядок.
Наступні рішення набагато швидше, ніж антистатичні (count (*) коштує багато, але якщо ви можете кешувати це, то різниця не повинна бути такою великою), що саме по собі набагато швидше, ніж "порядок випадково ()" коли у вас велика кількість рядків, хоча вони мають кілька незручностей.
Якщо ваші ряди досить упаковані (тобто кілька видалень), ви можете зробити наступне (використовуючи (select max(rowid) from foo)+1
замість того, щоб max(rowid)+1
покращити ефективність, як пояснено в коментарях):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Якщо у вас є отвори, іноді ви намагатиметеся вибрати неіснуючий рядин, і вибір поверне порожній набір результатів. Якщо це неприйнятно, ви можете надати таке значення за замовчуванням, як це:
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;
І щоб вибрати випадковий рядок, ви можете скористатися моїм першим методом (тут немає дірок). Звичайно, цей останній метод має певні проблеми з одночасністю, але відновлення 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/…
Вам потрібно поставити "замовлення на 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 ) десь і лише оновити його, коли зміниться кількість баз даних. Таким чином, вам не потрібно робити ВИБІР КОЛЕТИ кожен раз.
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, підрахунок). MAX
використовується для обробки справи з порожнім столом.
Ось прості результати тесту на таблиці з 16-ти рядковими рядками:
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, щоб запобігти рівню 0.