postgresql COUNT (DISTINCT ...) बहुत धीमा है


166

मेरे पास एक बहुत ही सरल SQL क्वेरी है:

SELECT COUNT(DISTINCT x) FROM table;

मेरी तालिका में लगभग 1.5 मिलियन पंक्तियाँ हैं। यह क्वेरी बहुत धीमी गति से चल रही है; इसकी तुलना में लगभग 7.5 का समय लगता है

 SELECT COUNT(x) FROM table;

जो लगभग 435 मी। क्या प्रदर्शन को बेहतर बनाने के लिए मेरी क्वेरी को बदलने का कोई तरीका है? मैंने समूह बनाने और एक नियमित गणना करने की कोशिश की है, साथ ही एक्स पर एक इंडेक्स भी लगाया है; दोनों में एक ही 7.5 का निष्पादन समय है।


मुझे ऐसा नहीं लगता। 1.5 मिलियन पंक्तियों के विशिष्ट मूल्यों को प्राप्त करना अभी धीमा होना है।
Ry-

5
मैंने अभी इसे C # में आज़माया है, मेमोरी से 1.5 मिलियन पूर्णांकों के अलग-अलग मान प्राप्त करना मेरे कंप्यूटर पर एक सेकंड से अधिक होता है। इसलिए मुझे लगता है कि आप शायद किस्मत से बाहर हैं।
Ry-

क्वेरी योजना बहुत हद तक तालिका संरचना (अनुक्रमित) और ट्यूनिंग स्थिरांक (कार्य) मेम, प्रभावी_कैश_साइज़, रैंडम_पेज_कॉस्ट की सेटिंग पर निर्भर करेगी। उचित ट्यूनिंग के साथ क्वेरी को संभवतः एक सेकंड से भी कम समय में निष्पादित किया जा सकता है।
वाइल्डपलासर

क्या आप अधिक विस्तार से बताएंगे? इसे एक सेकंड के तहत प्राप्त करने के लिए किन अनुक्रमों और ट्यूनिंग स्थिरांक की आवश्यकता होगी? सादगी के लिए, मान लें कि यह पहली कॉलम y पर प्राथमिक कुंजी के साथ एक दो-स्तंभ तालिका है, और मैं 1.5 मिलियन पंक्तियों के साथ टाइप के int के दूसरे स्तंभ x पर यह 'विशिष्ट' क्वेरी कर रहा हूं।
ferson2020

1
कृपया, सभी इंडेक्स के साथ तालिका की परिभाषा को शामिल करें ( \dआउटपुट psqlएक अच्छा है) और उस कॉलम को सटीक करें जिसके साथ आपको समस्या है। EXPLAIN ANALYZEदोनों प्रश्नों को देखना अच्छा होगा ।
वायगोरोव

जवाबों:


316

आप इसका उपयोग कर सकते हैं:

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

इससे बहुत तेज है:

COUNT(DISTINCT column_name)

38
पवित्र प्रश्नों के बल्लेबाज! इसने मेरे पोस्टगर्ल्स को 190 से 4.5 के बीच में भिन्न माना है!
रॉगरडैक

20
मुझे यह धागा www.postgresql.org पर मिला, जो एक ही चीज पर चर्चा करता है: लिंक । उत्तरों में से एक (जेफ जैनेस द्वारा) कहता है कि COUNT (DISTINCT ()) हैश का उपयोग करने के बजाय अपना काम करने के लिए तालिका को सॉर्ट करता है।
अंकुर

5
@ ठाकुर क्या मैं आपसे सवाल पूछ सकता हूं? चूंकि COUNT(DISTINCT())छंटाई करता है, इसलिए यह निश्चित रूप से मददगार होगा कि column_nameविशेष रूप से अपेक्षाकृत कम मात्रा work_mem(जहां हैशिंग बड़ी मात्रा में बैचों का उत्पादन करेगा) के साथ एक सूचकांक है । उसके बाद से, COUNT (DISTINCT () _, is?
St.Antario

2
@musmn Count(column)केवल गैर अशक्त मूल्यों को गिनाता है । count(*)पंक्तियों को गिनता है। तो पहला / लंबा एक, शून्य पंक्ति (एक बार) भी गिना जाएगा। count(column_name)उन्हें एक ही व्यवहार करने के लिए बदलें ।
गोलेज़ट्रॉल

1
@ankur यह मेरे लिए ज्यादा उपयोगी नहीं था..कभी कोई उल्लेखनीय सुधार नहीं हुआ।
शिवांगिनी

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
क्या आपने कैशिंग के प्रभाव पर विचार किया है? यदि बाद में तीन "एक्सप्लेन एनालिसिस" करें, तो पहले वाले को डिस्क से धीमी गति से लाने वाली चीजें हो सकती हैं, जबकि बाद वाले को मेमोरी से तेजी से ला सकता है।
टोबिकेन

वास्तव में: effective_cache_size ट्विक करने के लिए पहली सेटिंग है। मेरा 2GB, IIRC है।
वाइल्डपलासर

मैंने प्रदर्शन में कोई बदलाव नहीं करने के साथ अपना प्रभावी_कैश_साइज़ 2GB तक सेट किया। कोई अन्य सेटिंग जो आप सुझाएंगे? यदि हां, तो क्या?
ferson2020

1) आपने इसे कैसे सेट किया? (क्या आपने इसे HUP किया है?) 2) क्या आपके पास वास्तव में इतनी मेमोरी उपलब्ध है? 3) हमें अपनी योजना दिखाएं। 4) हो सकता है कि मेरी मशीन तेज हो, या आपके पास निपटने के लिए अधिक समवर्ती भार हो। @ ferson2020: ठीक है
Wildplasser

मैंने इसे स्टेटमेंट के साथ सेट किया है: SET प्रभावशाली_cache_size = '2GB'; मेरे पास उतनी स्मृति उपलब्ध है। मैंने अपनी क्वेरी योजना सहित कोशिश की, लेकिन यह टिप्पणी बॉक्स में फिट नहीं होगी।
ferson2020

2

यदि आपकी count(distinct(x))तुलना में काफी धीमी है, count(x)तो आप इस क्वेरी को अलग-अलग तालिका में x मान गणनाओं को बनाए रखकर गति प्रदान कर सकते हैं, उदाहरण के लिए table_name_x_counts (x integer not null, x_count int not null), ट्रिगर का उपयोग करके। लेकिन आपके लेखन प्रदर्शन को नुकसान होगा और यदि आप xएकल लेनदेन में कई मूल्यों को अपडेट करते हैं तो आपको संभावित गतिरोध से बचने के लिए कुछ स्पष्ट क्रम में ऐसा करना होगा।


0

मैं भी यही उत्तर खोज रहा था, क्योंकि किसी समय मुझे लिमिट / ऑफसेट के साथ अलग-अलग मूल्यों के साथ Total_count की आवश्यकता थी ।

क्योंकि यह करने के लिए थोड़ा मुश्किल है- सीमा / ऑफसेट के साथ अलग-अलग मूल्यों के साथ कुल गिनती प्राप्त करने के लिए। आमतौर पर सीमा / ऑफसेट के साथ कुल गिनती प्राप्त करना कठिन है। अंत में मुझे करने का तरीका मिल गया -

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

क्वेरी प्रदर्शन भी अधिक है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.