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보다 훨씬 빠릅니다 (count (*)는 비용이 많이 들지만 캐시 할 수 있다면 차이가 크지 않아야합니다). 자체적으로 "order by random ()"보다 훨씬 빠릅니다. 많은 수의 행이 있지만 몇 가지 불편한 점이 있습니다.
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 * 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와 정수 기본 키 자동 증가의 동작은 동일하지 않습니다 (rowid를 사용하면 새 행이 삽입 될 때 max (rowid) +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;
그런 다음 [0, n)에서 난수 m 을 선택 하고
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 사이의 의사 난수 정수를 반환합니다.
연산자 %는 오른쪽 피연산자와 모듈로 왼쪽 피연산자의 정수 값을 출력합니다.
마지막으로 rowid가 0이되지 않도록 +1을 추가합니다.