수백만 개의 행을 포함하는 postgres에 테이블이 있습니다. 인터넷에서 확인한 결과 다음을 발견했습니다.
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
작동하지만 정말 느립니다 ... 그 쿼리를 만드는 다른 방법이 있습니까, 아니면 모든 테이블을 읽지 않고 임의의 행을 선택하는 직접적인 방법이 있습니까? 그런데 'myid'는 정수이지만 빈 필드 일 수 있습니다.
수백만 개의 행을 포함하는 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 행을 반환합니다.
SELECT COUNT(*)
? 보다 작은 N을 사용하는 것이 합리적 입니다. 테이블의 모든 값을 사용하는 것이 아니라 일부만 사용합니까?
EXPLAIN SELECT ...
N의 다른 값과 함께 사용하면 쿼리에 동일한 비용이 발생합니다. 그러면 N의 최대 값을 찾는 것이 더 낫다고 생각합니다.
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/ 에서 차이점에 대해 읽어보십시오.
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
?
SELECT reltuples FROM pg_class WHERE relname = 'my_table'
개수 추정에 사용 합니다.
나는 이것을 하위 쿼리로 시도했고 잘 작동했습니다. 오프셋, 적어도 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;
random()*N
0 <= x <2를 생성하고 예를 들어 SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
가장 가까운 int로 내재적으로 반올림하기 때문에 0 행을 반환합니다.
order by random()
, 대략 3*O(N) < O(NlogN)
실제 수치는 인덱스로 인해 약간 다를 수 있습니다.
WHERE myid NOT IN (1st-myid)
및 WHERE myid NOT IN (1st-myid, 2nd-myid)
결정은 오프셋에 의해 이루어지기 때문에 작업을 것 없습니다. 음 ... 두 번째와 세 번째 SELECT에서 N을 1과 2로 줄일 수 있다고 생각합니다.
floor()
있습니까? 어떤 이점이 있습니까?
이 링크에서 몇 가지 다른 옵션을 확인하십시오. 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
ORDER BY random() LIMIT 1;
충분히 빠릅니다.
.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)