2012-06-28 31 views
99

Tôi có một truy vấn SQL rất đơn giản:COUNT địa chỉ postgresql (DISTINCT ...) rất chậm

SELECT COUNT(DISTINCT x) FROM table; 

bảng của tôi có khoảng 1,5 triệu hàng. Truy vấn này chạy khá chậm; phải mất khoảng 7,5s, 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 đã cố gắng nhóm và thực hiện một số thường xuyên, cũng như đặt một chỉ mục trên x; cả hai đều có cùng thời gian thực hiện 7,5 giây.

+0

Tôi không nghĩ như vậy. Nhận được các giá trị khác biệt của 1,5 triệu hàng chỉ là chậm. – Ryan

+4

Tôi đã 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ớ * mất 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 may mắn. – Ryan

+0

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à thiết lập hằng số điều chỉnh (công việc) mem, effective_cache_size, random_page_cost). Với việc điều chỉnh hợp lý, truy vấn có thể được thực hiện trong chưa đến một giây. – wildplasser

Trả lời

8
-- 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 
     ; 

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ó lẽ cũng được sản xuất bằng các phương pháp khác (chức năng cửa sổ)

+2

Bạn đã xem xét hiệu quả của bộ đệm ẩn chưa? Nếu làm ba "giải thích phân tích" sau đó, người đầu tiên có thể chậm lấy những thứ từ đĩa trong khi hai sau này có thể được lấy nhanh từ bộ nhớ. – tobixen

+0

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

+0

Tôi đặt my_cache_size thành 2GB, không thay đổi hiệu suất. Bạn có đề xuất chỉnh sửa bất kỳ cài đặt nào khác không? Nếu vậy, với cái gì? – ferson2020

1

Nếu bạn count(distinct(x)) là đáng kể chậm hơn so với count(x) sau đó 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 bảng khác nhau, ví dụ: table_name_x_counts (x integer not null, x_count int not null), sử dụng trình 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 giá trị x trong giao dịch đơn lẻ thì bạn cần thực hiện điều này theo thứ tự rõ ràng để tránh bế tắc có thể xảy ra.

201

Bạn có thể sử dụng này:

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

Đây là nhanh hơn nhiều so:

COUNT(DISTINCT column_name) 
+23

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

+11

Tôi rất thích một lời giải thích là tại sao điều này hoạt động. Mẹo tuyệt vời! – DavidMann10k

+11

Tôi tìm thấy chủ đề này trên [www.postgresql.org] (http://www.postgresql.org) thảo luận về cùng một điều: [link] (http://www.postgresql.org/message-id/CAONnt+ [email protected]). Một trong những câu trả lời (bởi 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