postgresql COUNT (DISTINCT…) 매우 느림


166

매우 간단한 SQL 쿼리가 있습니다.

SELECT COUNT(DISTINCT x) FROM table;

내 테이블에는 약 150 만 개의 행이 있습니다. 이 쿼리는 매우 느리게 실행됩니다. 약 7.5 초가 소요됩니다.

 SELECT COUNT(x) FROM table;

약 435ms가 걸립니다. 성능을 향상시키기 위해 쿼리를 변경하는 방법이 있습니까? 나는 x에 인덱스를 넣을뿐만 아니라 그룹화하고 규칙적인 수를 시도했습니다. 둘 다 동일한 7.5 초의 실행 시간을 갖습니다.


나는 그렇게 생각하지 않습니다. 150 만 행의 고유 한 값을 얻는 것은 느릴 것입니다.
Ry-

5
방금 C #에서 시도했지만 메모리에서 150 만 개의 정수 값을 컴퓨터에서 1 초 이상 가져옵니다. 그래서 당신은 아마 운이 좋지 않다고 생각합니다.
Ry-

쿼리 계획은 테이블 구조 (인덱스) 및 튜닝 상수 (작업) mem, effective_cache_size, random_page_cost의 설정에 따라 크게 달라집니다. 적절한 조정으로 쿼리를 1 초 이내에 실행할 수 있습니다.
wildplasser 2018 년

좀 더 구체적으로 말씀해 주시겠습니까? 순식간에 얻으려면 어떤 인덱스와 튜닝 상수가 필요합니까? 간단히하기 위해 이것이 첫 번째 열 y에 기본 키가있는 2 열 테이블이라고 가정하고 int 유형의 두 번째 열 x에서 150 만 행 으로이 '고유 한'쿼리를 수행하고 있습니다.
ferson2020

1
모든 색인과 함께 테이블 정의를 포함하고 ( \d출력 psql이 양호 함) 문제가있는 열을 정확하게 입력하십시오. EXPLAIN ANALYZE두 쿼리를 모두 보는 것이 좋습니다 .
vyegorov

답변:


316

이것을 사용할 수 있습니다 :

SELECT COUNT(*) FROM (SELECT DISTINCT column_name FROM table_name) AS temp;

이것은 다음보다 훨씬 빠릅니다.

COUNT(DISTINCT column_name)

38
거룩한 질문 배트맨! 이것은 내 postgres 수를 190에서 4.5로 바 꾸었습니다.
rogerdpack

20
www.postgresql.org 에서이 스레드를 발견 했는데 같은 내용을 설명합니다 : link . 답글 중 하나 인 Jeff Janes는 COUNT (DISTINCT ())가 해시 대신 테이블을 정렬하여 작업을 수행한다고 말합니다.
Ankur

5
@Ankur 질문 하나해도 될까요? COUNT(DISTINCT())정렬을 수행 하기 때문에 column_name특히 상대적으로 적은 양 work_mem(해시가 많은 양의 배치를 생성 하는 경우)에 대한 인덱스를 갖는 것이 도움이 될 것 입니다. COUNT (DISTINCT () _, 그렇지 않나요?)를 사용하는 것이 항상 나쁜 것은 아닙니다.
St.Antario

2
@musmahn Count(column)은 null이 아닌 값만 계산합니다. count(*)행을 계산합니다. 따라서 첫 번째 / 더 긴 것은 null 행을 계산합니다 (한 번). count(column_name)동일하게 작동하도록 변경하십시오 .
GolezTrol

1
@ankur 이것은 나에게별로 유용하지 않았습니다 .. 눈에 띄는 개선을 얻지 못했습니다.
Shiwangini

11
-- 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와 동일한 계획은 다른 방법으로도 생성 될 수 있습니다 (창 기능).


2
캐싱의 영향을 고려 했습니까? 세 번의 "설명 분석"을 수행하는 경우, 첫 번째는 디스크에서 항목을 느리게 가져 오는 반면 두 번째는 메모리에서 빨리 가져 오는 것입니다.
tobixen 2016 년

실제로 : effective_cache_size는 첫 번째 조정 설정입니다. 광산은 2GB, IIRC입니다.
wildplasser 2018 년

performance_cache_size를 2GB로 설정했는데 성능은 변경되지 않았습니다. 다른 설정을 조정 하시겠습니까? 그렇다면 무엇에?
ferson2020

1) 어떻게 설정 했습니까? (HUP 했습니까?) 2) 실제로 사용 가능한 메모리가 있습니까? 3) 당신의 계획을 보여주십시오. 4) 어쩌면 내 컴퓨터가 더 빠르거나 더 많은 부하를 처리 할 수 ​​있습니다. @ ferson2020 : Ok
wildplasser 2018 년

다음 명령문으로 설정했습니다. SET effective_cache_size = '2GB'; 사용 가능한 메모리가 충분합니다. 쿼리 계획을 포함하려고 시도했지만 주석 상자에 맞지 않습니다.
ferson2020 2016 년

2

귀하의 경우 count(distinct(x))IS는 상당히 느린 것보다 count(x)당신은 예를 들어, 다른 테이블에서 x 값의 수를 유지함으로써이 쿼리 속도를 높일 수 있습니다 table_name_x_counts (x integer not null, x_count int not null), 트리거를 사용합니다. 그러나 쓰기 성능이 저하되고 x단일 트랜잭션에서 여러 값 을 업데이트하는 경우 교착 상태를 피하기 위해 명시적인 순서로이 작업을 수행해야합니다.


0

또한 어느 시점에서 limit / offset과 함께 고유 한 값을 가진 total_count가 필요했기 때문에 동일한 답변을 검색했습니다 .

한계 / 오프셋과 함께 고유 한 값으로 총 개수를 얻는 것이 약간 까다롭기 때문에. 일반적으로 한계 / 오프셋으로 총계를 구하기는 어렵습니다. 마침내 나는 할 길을 얻었다-

SELECT DISTINCT COUNT(*) OVER() as total_count, * FROM table_name limit 2 offset 0;

쿼리 성능도 높습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.