MySQL - चयन में कहाँ (सबक्वेरी) फ़ील्ड - बहुत धीमी गति से क्यों?


133

मुझे एक डेटाबेस में कुछ डुप्लिकेट मिले हैं, जिनका मैं निरीक्षण करना चाहता हूं, इसलिए मैंने जो डुप्लिकेट हैं, उन्हें देखने के लिए मैंने क्या किया:

SELECT relevant_field
FROM some_table
GROUP BY relevant_field
HAVING COUNT(*) > 1

इस तरह, मैं प्रासंगिक पंक्तियों के साथ सभी पंक्तियों को एक से अधिक बार प्रस्तुत करूँगा। यह क्वेरी निष्पादित करने के लिए मिलीसेकंड लेता है।

अब, मैं प्रत्येक डुप्लिकेट का निरीक्षण करना चाहता था, इसलिए मैंने सोचा कि मैं उपरोक्त पंक्ति में एक प्रासंगिक_फ़ील्ड के साथ some_table में प्रत्येक पंक्ति का चयन कर सकता हूं, इसलिए मुझे यह पसंद आया:

SELECT *
FROM some_table 
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)

यह किसी कारण से बहुत धीमी गति से निकलता है (मिनट लगते हैं)। क्या वास्तव में यहाँ चल रहा है कि इसे धीमा करने के लिए? प्रासंगिक_फ़ील्ड अनुक्रमित है।

आखिरकार मैंने पहली क्वेरी से "temp_view" दृश्य बनाने की कोशिश की (SELECT relevant_field FROM some_table GROUP BY relevant_field HAVING COUNT(*) > 1), और फिर इसके बजाय अपनी दूसरी क्वेरी बना रहा हूं:

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM temp_view
)

और यह ठीक काम करता है। MySQL कुछ मिलीसेकंड में ऐसा करता है।

यहां कोई भी SQL विशेषज्ञ समझा सकता है कि क्या हो रहा है?


तुम क्या चाहते हो? एक को छोड़कर डुप्लिकेट प्रविष्टियों को हटाना चाहते हैं ?? सुझाव: कृपया सेल्फ ज्वाइन करें
diEcho

1
जाहिर है कि यह समूह धीमा है ...
अजूरी

पहली क्वेरी मिलीसेकंड में निष्पादित होती है (एक समूहन और फ़िल्टरिंग HAVING के साथ)। यह केवल अन्य क्वेरी के साथ संयोजन में है जो सब कुछ धीमा करता है (मिनट लगते हैं)।
रात 11:03

@diEcho, मैं डुप्लिकेट ढूंढना चाहता हूं, उनका निरीक्षण करता हूं, और कुछ मैन्युअल रूप से हटाता हूं।
रात 11:06

जवाबों:


112

इसमें क्वेरी फिर से लिखें

SELECT st1.*, st2.relevant_field FROM sometable st1
INNER JOIN sometable st2 ON (st1.relevant_field = st2.relevant_field)
GROUP BY st1.id  /* list a unique sometable field here*/
HAVING COUNT(*) > 1

मुझे लगता st2.relevant_fieldहै कि चयन में होना चाहिए, क्योंकि अन्यथा havingखंड एक त्रुटि देगा, लेकिन मुझे 100% यकीन नहीं है

INएक उप-वर्ग के साथ कभी भी उपयोग न करें ; यह कुख्यात है।
केवल INमूल्यों की निश्चित सूची के साथ कभी भी उपयोग करें ।

अधिक सुझाव

  1. यदि आप तेज़ी से प्रश्न करना चाहते हैं, तो SELECT *केवल उन फ़ील्ड्स का चयन न करें जिनकी आपको वास्तव में आवश्यकता है।
  2. सुनिश्चित करें कि आपके पास relevant_fieldसमान-जुड़ने में तेजी लाने के लिए एक सूचकांक है ।
  3. group byप्राथमिक कुंजी पर सुनिश्चित करें ।
  4. यदि आप InnoDB पर हैं और आप MySQL की तुलना में केवल अनुक्रमित फ़ील्ड्स का चयन करते हैं (और चीज़ें बहुत जटिल नहीं हैं) तो केवल अनुक्रमित चीज़ों का उपयोग करके आपकी क्वेरी को हल कर देगा, चीजों को तेज़ी से बढ़ाएगा।

आपके IN (select प्रश्नों के 90% के लिए सामान्य समाधान

इस कोड का उपयोग करें

SELECT * FROM sometable a WHERE EXISTS (
  SELECT 1 FROM sometable b
  WHERE a.relevant_field = b.relevant_field
  GROUP BY b.relevant_field
  HAVING count(*) > 1) 

1
आप इसके साथ भी लिख सकते हैं HAVING COUNT(*) > 1। यह आमतौर पर MySQL में तेज है।
ypercube y

@ypercube, निचले क्वेरी के लिए किया गया है, मुझे लगता है कि शीर्ष क्वेरी के लिए यह परिणाम को बदल देगा।
जोहान

@ जोहान: चूंकि st2.relevant_fieldयह नहीं है NULL(यह पहले से ही ONखंड में शामिल है ), यह परिणाम को बदल नहीं देगा।
ypercube y

@ypercube, तो आप गिनती (afield) को गिनती (*) में बदल सकते हैं यदि आपको यकीन है कि afieldकभी नहीं होगा null, मिल गया। धन्यवाद
जोहान

1
@quano, हाँ यह सभी डुप्लिकेट को सूचीबद्ध करता है क्योंकि group byचालू है st1.id, चालू नहीं है st1.relevant_field
जोहान

110

सबक्वेरी को प्रत्येक पंक्ति के लिए चलाया जा रहा है क्योंकि यह एक सहसंबद्ध क्वेरी है। सब-वेरी से सब कुछ का चयन करके एक गैर-सहसंबद्ध क्वेरी में एक परस्पर संबंधित क्वेरी बना सकता है, जैसे:

SELECT * FROM
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
) AS subquery

अंतिम क्वेरी इस तरह दिखाई देगी:

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT * FROM
    (
        SELECT relevant_field
        FROM some_table
        GROUP BY relevant_field
        HAVING COUNT(*) > 1
    ) AS subquery
)

3
इसने मेरे लिए आश्चर्यजनक रूप से अच्छा काम किया। एक IN (सबक्वेरी) के भीतर मेरा एक और IN (सबक्वेरी) था, और इसमें 10 मिनट से ज्यादा का समय लग रहा था, इतना लंबा कि मैं इंतजार करते-करते रुक गया। जैसा कि आपने सुझाया है कि प्रत्येक उप-वर्ग को SELECT * FROM () में लपेटकर इसे 2 सेकंड तक कम कर दें!
लियाम

धन्यवाद, मैं एक दो घंटे के लिए यह करने के लिए एक अच्छा तरीका जानने की कोशिश कर रहा हूँ। यह पूरी तरह से काम किया। काश मैं तुम्हें और अधिक चढ़ाव दे सकता! इसका उत्तर निश्चित रूप से होना चाहिए।
thaspius

अच्छी तरह से काम। एक क्वेरी जिसे चलाने के लिए ~ 50secs लिया गया था, वह अब तात्कालिक है। काश मैं और बढ़ जाता। कभी-कभी आप जॉइन का उपयोग नहीं कर सकते हैं इसलिए यह सही उत्तर है।
सिमोन

मुझे आश्चर्य है कि ऑप्टिमाइज़र यूनियनों के साथ प्रश्नों को सहसंबद्ध क्यों मानता है ... वैसे भी, इस ट्रिक ने जादू की तरह काम किया
ब्रायन लीशमैन

2
क्या आप कृपया बता सकते हैं कि एक सहसंबंधित उपशम क्या है? मेरी समझ यह है कि उपसमुच्चय सहसंबद्ध हो जाता है, जब यह एक मूल्य का उपयोग करता है जो बाहरी क्वेरी पर निर्भर करता है। लेकिन इस उदाहरण में मैं कोई अन्य निर्भरता नहीं देख सकता। यह बाहरी क्वेरी द्वारा लौटाए गए प्रत्येक पंक्ति के लिए समान परिणाम देगा। मेरा एक ऐसा ही उदाहरण है मारियाडीबी पर लागू किया जा रहा है और मैं कोई प्रदर्शन हिट (अब तक) नहीं देख सकता, इसलिए मैं स्पष्ट रूप से देखना चाहता हूं, जब इस SELECT *रैपिंग की आवश्यकता होती है।
sbnc.eu

6

मुझे कुछ इस तरह से संदेह हुआ, कि प्रत्येक पंक्ति के लिए सबकुछ चलाया जा रहा है।
रात 11:56

कुछ MySQL संस्करण में भी एक सूचकांक का उपयोग नहीं करते। मैंने एक और लिंक जोड़ा है।
edze

1
MySQL 6 अभी तक स्थिर नहीं है, मैं उत्पादन के लिए इसकी सिफारिश नहीं करूंगा!
जोहान

1
मेरा सुझाव यह नहीं होगा। लेकिन यहां बताया गया है कि यह आंतरिक रूप से कैसे चलता है (4.1 / 5.x -> 6)। यह वर्तमान संस्करणों के कुछ नुकसानों को प्रदर्शित करता है।
edze

5
SELECT st1.*
FROM some_table st1
inner join 
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)st2 on st2.relevant_field = st1.relevant_field;

मैंने अपने डेटाबेस में से एक पर आपकी क्वेरी की कोशिश की है, और एक उप-क्वेरी में शामिल होने के रूप में इसे फिर से लिखने की भी कोशिश की है।

यह एक बहुत तेजी से काम किया, यह कोशिश करो!


हां, यह संभवतः समूह परिणामों के साथ एक अस्थायी तालिका बनाएगा, इसलिए यह दृश्य संस्करण के समान गति होगी। लेकिन क्वेरी प्लान को सच बताना चाहिए।
ypercube y

3

इसे इस्तेमाल करे

SELECT t1.*
FROM 
 some_table t1,
  (SELECT relevant_field
  FROM some_table
  GROUP BY relevant_field
  HAVING COUNT (*) > 1) t2
WHERE
 t1.relevant_field = t2.relevant_field;

2

मैंने www.prettysql.net के साथ आपकी धीमी एसक्यूएल क्वेरी में सुधार किया है

SELECT *
FROM some_table
WHERE
 relevant_field in
 (
  SELECT relevant_field
  FROM some_table
  GROUP BY relevant_field
  HAVING COUNT ( * ) > 1
 );

क्वेरी और सबक्वेरी दोनों में एक तालिका का उपयोग करते समय, आपको हमेशा इस तरह दोनों को उर्फ ​​करना चाहिए:

SELECT *
FROM some_table as t1
WHERE
 t1.relevant_field in
 (
  SELECT t2.relevant_field
  FROM some_table as t2
  GROUP BY t2.relevant_field
  HAVING COUNT ( t2.relevant_field ) > 1
 );

क्या उससे मदद हुई?


1
यह दुर्भाग्य से मदद नहीं करता है। यह धीमी गति से ही निष्पादित होता है।
quano

मैंने अपना उत्तर अपडेट कर दिया है, क्या आप फिर से कोशिश कर सकते हैं? यहां तक कि अगर द्वारा समूह धीमी है, यह केवल एक बार निष्पादित किया जाना चाहिए ...
Plang

मैंने पिछली बार गलती से एक लाइव mysql सर्वर को मार दिया था, इसलिए मुझे डर है कि मैं अभी यह कोशिश नहीं कर सकता। मुझे बाद में एक परीक्षण डेटाबेस स्थापित करना होगा। लेकिन मुझे समझ नहीं आ रहा है कि इस क्वेरी को क्यों प्रभावित करना चाहिए। HAVING स्टेटमेंट केवल उस क्वेरी पर लागू होना चाहिए जो उसके भीतर है, नहीं? मुझे वास्तव में समझ में नहीं आता है कि "वास्तविक" क्वेरी को सबक्वेरी को क्यों प्रभावित करना चाहिए।
quano

मुझे यह मिला: xaprb.com/blog/2006/04/30/… । मुझे लगता है कि यह समाधान हो सकता है। समय मिलने पर प्रयास करूंगा।
quano

2

सबसे पहले आप डुप्लिकेट पंक्तियाँ पा सकते हैं और पंक्तियों की गिनती का उपयोग कितनी बार किया जाता है और इसे इस तरह की संख्या से क्रमबद्ध कर सकते हैं;

SELECT q.id,q.name,q.password,q.NID,(select count(*) from UserInfo k where k.NID= q.NID) as Count,
(
		CASE q.NID
		WHEN @curCode THEN
			@curRow := @curRow + 1
		ELSE
			@curRow := 1
		AND @curCode := q.NID
		END
	) AS No
FROM UserInfo q,
(
		SELECT
			@curRow := 1,
			@curCode := ''
	) rt
WHERE q.NID IN
(
    SELECT NID
    FROM UserInfo
    GROUP BY NID
    HAVING COUNT(*) > 1
) 

उसके बाद एक तालिका बनाएं और उसमें परिणाम डालें।

create table CopyTable 
SELECT q.id,q.name,q.password,q.NID,(select count(*) from UserInfo k where k.NID= q.NID) as Count,
(
		CASE q.NID
		WHEN @curCode THEN
			@curRow := @curRow + 1
		ELSE
			@curRow := 1
		AND @curCode := q.NID
		END
	) AS No
FROM UserInfo q,
(
		SELECT
			@curRow := 1,
			@curCode := ''
	) rt
WHERE q.NID IN
(
    SELECT NID
    FROM UserInfo
    GROUP BY NID
    HAVING COUNT(*) > 1
) 

अंत में, डीबेट की पंक्तियों को हटा दें। कोई भी शुरुआत नहीं है। प्रत्येक समूह की मुट्ठी संख्या को छोड़कर सभी डायबिटेट पंक्तियों को हटा दें।

delete from  CopyTable where No!= 0;


1

कभी-कभी जब डेटा बड़ा हो जाता है तो mysql जहां क्वेरी अनुकूलन के कारण बहुत धीमा हो सकता है। जैसे कि क्वेरी निष्पादित करने के लिए mysql बताने के लिए STRAIGHT_JOIN का उपयोग करने का प्रयास करें

SELECT STRAIGHT_JOIN table.field FROM table WHERE table.id IN (...)

लेकिन सावधान रहें: ज्यादातर मामलों में mysql ऑप्टिमाइज़र बहुत अच्छी तरह से काम करता है, इसलिए मैं इसे तभी इस्तेमाल करने की सलाह दूंगा जब आपको इस तरह की समस्या हो


0

यह मेरे मामले के समान है, जहां मेरे नाम की एक तालिका है tabel_buku_besar। मुझे क्या चाहिए

  1. रिकॉर्ड के लिए खोज रहे हैं account_code='101.100'में tabel_buku_besarजो किया है companyarea='20000'और यह भी है IDRके रूप मेंcurrency

  2. मुझे वह सभी रिकॉर्ड प्राप्त करने की आवश्यकता है, tabel_buku_besarजिसमें चरण 1 के समान खाता_कोड है, लेकिन transaction_numberचरण 1 परिणाम में है

उपयोग करते समय select ... from...where....transaction_number in (select transaction_number from ....), मेरी क्वेरी बहुत धीमी गति से चल रही है और कभी-कभी अनुरोध करने का समय निकल जाता है या मेरे आवेदन का जवाब नहीं दे रहा है ...

मैं इस संयोजन और परिणाम की कोशिश करता हूं ... बुरा नहीं ...

`select DATE_FORMAT(L.TANGGAL_INPUT,'%d-%m-%y') AS TANGGAL,
      L.TRANSACTION_NUMBER AS VOUCHER,
      L.ACCOUNT_CODE,
      C.DESCRIPTION,
      L.DEBET,
      L.KREDIT 
 from (select * from tabel_buku_besar A
                where A.COMPANYAREA='$COMPANYAREA'
                      AND A.CURRENCY='$Currency'
                      AND A.ACCOUNT_CODE!='$ACCOUNT'
                      AND (A.TANGGAL_INPUT BETWEEN STR_TO_DATE('$StartDate','%d/%m/%Y') AND STR_TO_DATE('$EndDate','%d/%m/%Y'))) L 
INNER JOIN (select * from tabel_buku_besar A
                     where A.COMPANYAREA='$COMPANYAREA'
                           AND A.CURRENCY='$Currency'
                           AND A.ACCOUNT_CODE='$ACCOUNT'
                           AND (A.TANGGAL_INPUT BETWEEN STR_TO_DATE('$StartDate','%d/%m/%Y') AND STR_TO_DATE('$EndDate','%d/%m/%Y'))) R ON R.TRANSACTION_NUMBER=L.TRANSACTION_NUMBER AND R.COMPANYAREA=L.COMPANYAREA 
LEFT OUTER JOIN master_account C ON C.ACCOUNT_CODE=L.ACCOUNT_CODE AND C.COMPANYAREA=L.COMPANYAREA 
ORDER BY L.TANGGAL_INPUT,L.TRANSACTION_NUMBER`

0

मुझे लगता है कि यदि कोई मान मौजूद है, तो यह पता लगाने के लिए सबसे अधिक कुशल है कि यदि कोई मान मौजूद नहीं है, तो तर्क को आसानी से उलटा किया जा सकता है (यानी IS NULL);

SELECT * FROM primary_table st1
LEFT JOIN comparision_table st2 ON (st1.relevant_field = st2.relevant_field)
WHERE st2.primaryKey IS NOT NULL

* प्रासंगिक तालिका को उस मान के नाम से बदलें, जिसे आप चेक करना चाहते हैं, जो आपकी तालिका में मौजूद है

* तुलना तालिका पर प्राथमिक कुंजी कॉलम के नाम के साथ प्राइमरीके को बदलें।

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