[performance] postgresql COUNT (DISTINCT…) 매우 느림
매우 간단한 SQL 쿼리가 있습니다.
SELECT COUNT(DISTINCT x) FROM table;
내 테이블에는 약 150 만 개의 행이 있습니다. 이 쿼리는 매우 느리게 실행됩니다. 약 7.5 초가 소요됩니다.
SELECT COUNT(x) FROM table;
약 435ms가 걸립니다. 성능을 향상시키기 위해 쿼리를 변경하는 방법이 있습니까? 나는 x에 인덱스를 넣을뿐만 아니라 그룹화하고 규칙적인 수를 시도했습니다. 둘 다 동일한 7.5 초의 실행 시간을 갖습니다.
답변
이것을 사용할 수 있습니다 :
SELECT COUNT(*) FROM (SELECT DISTINCT column_name FROM table_name) AS temp;
이것은 다음보다 훨씬 빠릅니다.
COUNT(DISTINCT column_name)
답변
-- My default settings (this is basically a single-session machine, so work_mem is pretty high)
SET effective_cache_size='2048MB';
SET work_mem='16MB';
\echo original
EXPLAIN ANALYZE
SELECT
COUNT (distinct val) as aantal
FROM one
;
\echo group by+count(*)
EXPLAIN ANALYZE
SELECT
distinct val
-- , COUNT(*)
FROM one
GROUP BY val;
\echo with CTE
EXPLAIN ANALYZE
WITH agg AS (
SELECT distinct val
FROM one
GROUP BY val
)
SELECT COUNT (*) as aantal
FROM agg
;
결과 :
original QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Aggregate (cost=36448.06..36448.07 rows=1 width=4) (actual time=1766.472..1766.472 rows=1 loops=1)
-> Seq Scan on one (cost=0.00..32698.45 rows=1499845 width=4) (actual time=31.371..185.914 rows=1499845 loops=1)
Total runtime: 1766.642 ms
(3 rows)
group by+count(*)
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=36464.31..36477.31 rows=1300 width=4) (actual time=412.470..412.598 rows=1300 loops=1)
-> HashAggregate (cost=36448.06..36461.06 rows=1300 width=4) (actual time=412.066..412.203 rows=1300 loops=1)
-> Seq Scan on one (cost=0.00..32698.45 rows=1499845 width=4) (actual time=26.134..166.846 rows=1499845 loops=1)
Total runtime: 412.686 ms
(4 rows)
with CTE
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=36506.56..36506.57 rows=1 width=0) (actual time=408.239..408.239 rows=1 loops=1)
CTE agg
-> HashAggregate (cost=36464.31..36477.31 rows=1300 width=4) (actual time=407.704..407.847 rows=1300 loops=1)
-> HashAggregate (cost=36448.06..36461.06 rows=1300 width=4) (actual time=407.320..407.467 rows=1300 loops=1)
-> Seq Scan on one (cost=0.00..32698.45 rows=1499845 width=4) (actual time=24.321..165.256 rows=1499845 loops=1)
-> CTE Scan on agg (cost=0.00..26.00 rows=1300 width=0) (actual time=407.707..408.154 rows=1300 loops=1)
Total runtime: 408.300 ms
(7 rows)
CTE와 동일한 계획은 다른 방법으로도 생성 될 수 있습니다 (창 기능).
답변
귀하의 경우 count(distinct(x))
IS는 상당히 느린 것보다 count(x)
당신은 예를 들어, 다른 테이블에서 x 값의 수를 유지함으로써이 쿼리 속도를 높일 수 있습니다 table_name_x_counts (x integer not null, x_count int not null)
, 트리거를 사용합니다. 그러나 쓰기 성능이 저하되고 x
단일 트랜잭션에서 여러 값 을 업데이트하는 경우 교착 상태를 피하기 위해 명시적인 순서로이 작업을 수행해야합니다.
답변
또한 어느 시점에서 limit / offset과 함께 고유 한 값을 가진 total_count가 필요했기 때문에 동일한 답변을 검색했습니다 .
한계 / 오프셋과 함께 고유 한 값으로 총 개수를 얻는 것이 약간 까다롭기 때문에. 일반적으로 한계 / 오프셋으로 총계를 구하기는 어렵습니다. 마침내 나는 할 길을 얻었다-
SELECT DISTINCT COUNT(*) OVER() as total_count, * FROM table_name limit 2 offset 0;
쿼리 성능도 높습니다.