postgresql COUNT (DISTINCT) rất chậm


166

Tôi có một truy vấn SQL rất đơn giản:

SELECT COUNT(DISTINCT x) FROM table;

Bàn của tôi có khoảng 1,5 triệu hàng. Truy vấn này đang chạy khá chậm; phải mất khoảng 7,5 giây, so với

 SELECT COUNT(x) FROM table;

Mất khoảng 435ms. Có cách nào để thay đổi truy vấn của tôi để cải thiện hiệu suất không? Tôi đã thử nhóm và thực hiện đếm thường xuyên, cũng như đặt chỉ mục trên x; cả hai đều có thời gian thực hiện 7.5s giống nhau.


Tôi không nghĩ vậy. Lấy các giá trị riêng biệt của 1,5 triệu hàng sẽ chậm.
Ry-

5
Tôi vừa thử nó trong C #, nhận được các giá trị riêng biệt của 1,5 triệu số nguyên từ bộ nhớ chiếm hơn một giây trên máy tính của tôi. Vì vậy, tôi nghĩ rằng bạn có thể không gặp may mắn.
Ry-

Kế hoạch truy vấn sẽ phụ thuộc rất nhiều vào cấu trúc bảng (chỉ mục) và cài đặt của hằng số điều chỉnh (công việc) mem, hiệu quả_cache_size, Random_page_cost). Với điều chỉnh hợp lý, truy vấn có thể có thể được thực hiện trong chưa đầy một giây.
wildplasser

Bạn có thể đặc sắc hơn không? Những chỉ số và hằng số điều chỉnh nào sẽ được yêu cầu để có được nó dưới một giây? Để đơn giản, giả sử đây là bảng hai cột có khóa chính trên cột đầu tiên và tôi đang thực hiện truy vấn 'khác biệt' này trên cột thứ hai x loại int, với 1,5 triệu hàng.
ferson2020

1
Vui lòng bao gồm định nghĩa bảng với tất cả các chỉ mục ( \dđầu ra psqllà tốt) và chính xác cột mà bạn gặp vấn đề. Sẽ tốt khi thấy EXPLAIN ANALYZEcả hai truy vấn.
vyegorov

Câu trả lời:


316

Bạn có thể sử dụng điều này:

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

Điều này nhanh hơn nhiều so với:

COUNT(DISTINCT column_name)

38
truy vấn thánh batman! Điều này tăng tốc số lượng postgres của tôi khác biệt từ 190 đến 4,5 whoa!
rogerdpack

20
Tôi tìm thấy chủ đề này trên www.postgresql.org , thảo luận về điều tương tự: liên kết . Một trong những câu trả lời (của Jeff Janes) nói rằng COUNT (DISTINCT ()) sắp xếp bảng để thực hiện công việc của nó thay vì sử dụng hàm băm.
Ankur

5
@Ankur Tôi có thể hỏi bạn câu hỏi? Kể từ khi COUNT(DISTINCT())thực hiện sắp xếp, chắc chắn sẽ rất hữu ích khi có một chỉ số trên column_nameđặc biệt với lượng tương đối nhỏ work_mem(trong đó băm sẽ tạo ra số lượng lớn các lô tương đối). Kể từ đó, không phải lúc nào cũng xấu khi sử dụng COUNT (DISTINCT () _, phải không?
St.Antario

2
@musmahn Count(column)chỉ tính các giá trị không null. count(*)đếm hàng. Vì vậy, cái đầu tiên / dài hơn, cũng sẽ tính hàng null (một lần). Thay đổi để count(column_name)làm cho họ cư xử như nhau.
GolezTrol

1
@ankur điều này không hữu ích lắm đối với tôi..không nhận được sự cải thiện đáng chú ý nào.
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
        ;

Các kết quả:

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)

Kế hoạch tương tự như đối với CTE có thể cũng có thể được tạo ra bởi các phương thức khác (chức năng cửa sổ)


2
Bạn đã xem xét hiệu quả của bộ nhớ đệm? Nếu thực hiện ba lần "giải thích phân tích" sau đó, lần đầu tiên có thể là tìm nạp chậm các thứ từ đĩa trong khi hai lần sau có thể tìm nạp nhanh từ bộ nhớ.
tobixen

Thật vậy: effect_cache_size là cài đặt đầu tiên để điều chỉnh. Của tôi là 2GB, IIRC.
wildplasser

Tôi đặt hiệu quả_cache_size của tôi thành 2GB, không có thay đổi về hiệu suất. Bất kỳ cài đặt nào khác bạn muốn đề xuất điều chỉnh? Nếu vậy, để làm gì?
ferson2020

1) làm thế nào bạn thiết lập nó? (bạn đã HUP nó chưa?) 2) Bạn có thực sự có sẵn nhiều bộ nhớ không? 3) cho chúng tôi thấy kế hoạch của bạn. 4) có thể máy của tôi nhanh hơn hoặc máy của bạn có tải đồng thời hơn để xử lý. @ ferson2020: Ok
wildplasser

Tôi đặt nó với câu lệnh: SET effect_cache_size = '2GB'; Tôi có sẵn rất nhiều bộ nhớ. Tôi đã thử bao gồm cả kế hoạch truy vấn của mình, nhưng nó sẽ không phù hợp trong hộp bình luận.
ferson2020

2

Nếu bạn count(distinct(x))chậm hơn đáng kể count(x)thì bạn có thể tăng tốc truy vấn này bằng cách duy trì số lượng giá trị x trong các bảng khác nhau, ví dụ table_name_x_counts (x integer not null, x_count int not null), bằng cách sử dụng kích hoạt. Nhưng hiệu suất ghi của bạn sẽ bị ảnh hưởng và nếu bạn cập nhật nhiều xgiá trị trong một giao dịch thì bạn cần thực hiện việc này theo một số thứ tự rõ ràng để tránh bế tắc có thể xảy ra.


0

Tôi cũng đang tìm kiếm câu trả lời tương tự, bởi vì tại một số thời điểm tôi cần Total_count với các giá trị riêng biệt cùng với giới hạn / bù .

Bởi vì nó hơi khó để làm- Để có được tổng số với các giá trị riêng biệt cùng với giới hạn / bù. Thông thường rất khó để có được tổng số với giới hạn / bù. Cuối cùng tôi cũng có cách làm -

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

Hiệu năng truy vấn cũng cao.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.