[postgresql] Postgres에서 빠른 무작위 행 선택
수백만 개의 행을 포함하는 postgres에 테이블이 있습니다. 인터넷에서 확인한 결과 다음을 발견했습니다.
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
작동하지만 정말 느립니다 … 그 쿼리를 만드는 다른 방법이 있습니까, 아니면 모든 테이블을 읽지 않고 임의의 행을 선택하는 직접적인 방법이 있습니까? 그런데 ‘myid’는 정수이지만 빈 필드 일 수 있습니다.
답변
OFFSET
에서 와 같이 실험 해 볼 수 있습니다 .
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
은 N
행의 수입니다 mytable
. SELECT COUNT(*)
의 값을 파악 하려면 먼저 a 를 수행해야 할 수 있습니다 N
.
업데이트 (Antony Hatchkins 제공)
floor
여기에서 사용해야 합니다.
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
2 행의 테이블을 고려하십시오. random()*N
생성 0 <= x < 2
예를 들어 SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
있기 때문에 가까운 INT로 암시 적 라운딩의 0 행을 반환합니다.
답변
PostgreSQL 9.5는 훨씬 더 빠른 샘플 선택을위한 새로운 접근 방식을 도입했습니다 : TABLESAMPLE
구문은
SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage);
SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
정확한 백분율을 계산하려면 테이블의 COUNT를 알아야하기 때문에 하나의 행만 선택하려는 경우 최적의 솔루션이 아닙니다.
느린 COUNT를 방지하고 1 행에서 수십억 행까지의 테이블에 대해 빠른 TABLESAMPLE을 사용하려면 다음을 수행 할 수 있습니다.
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.000001) LIMIT 1;
-- if you got no result:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.00001) LIMIT 1;
-- if you got no result:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.0001) LIMIT 1;
-- if you got no result:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.001) LIMIT 1;
...
이것은 그렇게 우아하게 보이지 않을 수도 있지만 아마도 다른 답변보다 빠를 것입니다.
BERNULLI 또는 SYSTEM을 사용할 것인지 결정하려면 http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/ 에서 차이점에 대해 읽어보십시오.
답변
나는 이것을 하위 쿼리로 시도했고 잘 작동했습니다. 오프셋, 적어도 Postgresql v8.4.4에서는 정상적으로 작동합니다.
select * from mytable offset random() * (select count(*) from mytable) limit 1 ;
답변
다음을 사용해야합니다 floor
.
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
답변
이 링크에서 몇 가지 다른 옵션을 확인하십시오.
http://www.depesz.com/index.php/2007/09/16/my-thoughts-on-getting-random-row/
최신 정보: (A. 해치 킨스)
(매우) 긴 기사의 요약은 다음과 같습니다.
저자는 네 가지 접근 방식을 나열합니다.
1) ORDER BY random() LIMIT 1;
-느림
2) ORDER BY id where id>=random()*N LIMIT 1
-간격이있는 경우 불균일
3) 임의의 열-가끔 업데이트해야 함
4) 사용자 정의 임의 집계 -교활한 방법, 느릴 수 있음 : random ()을 N 번 생성해야 함
방법 # 2를 사용하여 개선 할 것을 제안합니다.
5) ORDER BY id where id=random()*N LIMIT 1
결과가 비어있는 경우 후속 재 쿼리.
답변
임의의 행을 가져 오는 가장 쉽고 빠른 방법은 tsm_system_rows
확장 을 사용하는 것 입니다.
CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
그런 다음 원하는 정확한 행 수를 선택할 수 있습니다.
SELECT myid FROM mytable TABLESAMPLE SYSTEM_ROWS(1);
PostgreSQL 9.5 이상에서 사용할 수 있습니다.
참조 : https://www.postgresql.org/docs/current/static/tsm-system-rows.html
답변
.NET없이 매우 빠른 솔루션을 찾았습니다 TABLESAMPLE
. 보다 훨씬 빠릅니다 OFFSET random()*N LIMIT 1
. 테이블 카운트도 필요하지 않습니다.
아이디어는 무작위이지만 예측 가능한 데이터로 표현식 인덱스를 만드는 것입니다 md5(primary key)
.
다음은 1M 행 샘플 데이터를 사용한 테스트입니다.
create table randtest (id serial primary key, data int not null);
insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000);
create index randtest_md5_id_idx on randtest (md5(id::text));
explain analyze
select * from randtest where md5(id::text)>md5(random()::text)
order by md5(id::text) limit 1;
결과:
Limit (cost=0.42..0.68 rows=1 width=8) (actual time=6.219..6.220 rows=1 loops=1)
-> Index Scan using randtest_md5_id_idx on randtest (cost=0.42..84040.42 rows=333333 width=8) (actual time=6.217..6.217 rows=1 loops=1)
Filter: (md5((id)::text) > md5((random())::text))
Rows Removed by Filter: 1831
Total runtime: 6.245 ms
이 쿼리는 때때로 (약 1 / Number_of_rows 확률로) 0 행을 반환 할 수 있으므로 확인하고 다시 실행해야합니다. 또한 확률은 정확히 동일하지 않습니다. 일부 행은 다른 행보다 확률이 높습니다.
비교하려고:
explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;
결과는 매우 다양하지만 매우 나쁠 수 있습니다.
Limit (cost=1442.50..1442.51 rows=1 width=4) (actual time=179.183..179.184 rows=1 loops=1)
-> Seq Scan on randtest (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.016..134.835 rows=915702 loops=1)
Total runtime: 179.211 ms
(3 rows)