[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)