web-dev-qa-db-fra.com

comte postgresql (DISTINCT ...) très lent

J'ai une requête SQL très simple:

SELECT COUNT(DISTINCT x) FROM table;

Ma table a environ 1,5 million de lignes. Cette requête s'exécute assez lentement. il faut environ 7,5 secondes, comparé à

 SELECT COUNT(x) FROM table;

ce qui prend environ 435ms. Est-il possible de modifier ma requête pour améliorer les performances? J'ai essayé de regrouper et de compter régulièrement, ainsi que de mettre un index sur x; les deux ont le même temps d'exécution 7.5s.

118
ferson2020

Vous pouvez utiliser ceci:

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

C'est beaucoup plus rapide que:

COUNT(DISTINCT column_name)
241
Ankur
-- 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
        ;

Résultats:

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)

Le même plan que pour le CTE pourrait probablement aussi être produit par d’autres méthodes (fonctions de fenêtre)

10
wildplasser

Si votre count(distinct(x)) est nettement plus lent que count(x), vous pouvez accélérer cette requête en conservant le nombre de valeurs x dans une table différente, par exemple table_name_x_counts (x integer not null, x_count int not null) à l'aide de déclencheurs. Mais vos performances en écriture en souffriront et si vous mettez à jour plusieurs valeurs x dans une transaction unique, vous devrez le faire dans un ordre explicite pour éviter un éventuel blocage.

1
Tometzky