2009-07-29 13 views
1

Ich habe zwei Abfragen zu bezogenen Tags aus einer MySQL-Datenbank, eine funktioniert, eine nicht, meine Frage ist: "warum?"Mysql Sub-Select Problem

Problem: Wenn die erste Abfrage ausgeführt wird, der MySQL-Server erhält 100% CPU-Auslastung und muss neu gestartet werden wieder funktionieren.

Abfrage 1 (funktioniert nicht):

SELECT tags.*, COUNT(ct.company_id) AS count 
FROM company2tag ct, tags 
WHERE ct.company_id IN (
    SELECT ct.company_id FROM company2tag ct 
    WHERE ct.tag_id = 18 
    GROUP BY ct.company_id 
    HAVING COUNT(ct.company_id) = 1 
) 
AND tags.id != 18 
AND tags.id = ct.tag_id 
GROUP BY ct.tag_id 
ORDER BY count DESC 
LIMIT 5; 

Abfrage 2 (Werke):

SELECT tags.*, COUNT(ct.company_id) AS count 
FROM company2tag ct, tags 
WHERE ct.company_id IN (5864, 5870, 6140, 6221, 6268) 
    AND tags.id != 18 
    AND tags.id = ct.tag_id 
GROUP BY ct.tag_id 
ORDER BY count DESC 
LIMIT 5; 

Zu meinem Verständnis der beiden Abfragen über sie völlig gleich, die nur Der Unterschied besteht darin, dass die erste Abfrage ihre "company_id's" über einen Subselect abruft.

Wie kann das passieren?

+0

Und wie geht die Unterabfrage von alleine? – paxdiablo

+0

funktioniert einwandfrei, es gibt eine Liste von company_id's zurück. – smoove

+0

Ich kann nicht herausfinden, wie Ihre zweite Abfrage funktioniert, weil die SQL nicht gültig ist. Alle ausgewählten Felder, die keine Aggregatfunktionen sind, sollten in der GROUP BY-Liste (Tags. * In Ihrem Fall) vorhanden sein. – Tihauan

Antwort

3

Zunächst treten möglicherweise Probleme bei der ersten Abfrage auf, weil Sie zwei Tabellen mit Aliasnamen für ct ... in der äußeren Abfrage und in der Unterabfrage haben.

Zweitens können Sie die IN als JOIN umschreiben:

SELECT tags.*, COUNT(ct.company_id) AS count 
FROM company2tag ct 
INNER JOIN tags ON tags.id = ct.tag_id 
INNER JOIN (
    SELECT company_id FROM company2tag 
    WHERE tag_id = 18 
    GROUP BY company_id 
    HAVING COUNT(company_id) = 1 
) ctf ON ct.company_id = ctf.company_id 
WHERE tags.id != 18 
GROUP BY ct.tag_id 
ORDER BY count DESC 
LIMIT 5; 

Bitte beachte, dass ich das nicht wirklich getestet.

+0

+1 für die Erwähnung des Umschreibens des IN als JOIN. IN soll für eine Übereinstimmung mit einer kurzen Liste sein. Wenn Sie eine größere Liste basierend auf anderen Tabellen/Abfragen haben, sollte es ein JOIN sein. –

+0

Danke dafür! Ihre Lösung ist etwa 10-mal schneller und blockiert den Server nicht. – smoove

2

MySQL ist nicht sehr gut in der Optimierung IN Bedingungen.

Die Bedingung in Ihrer ersten Abfrage kann nicht einfach als EXISTS umgeschrieben werden, deshalb überprüft MySQL die Ergebnisse für jede Zeile.

Wenn Sie company_id ‚s auswählen möchten, die mehr als einmal in tag 18 erwähnt sind, ist es besser, diese Abfrage als solche neu zu schreiben:

SELECT tags.*, COUNT(company_id) AS count 
FROM company2tag ct 
JOIN tags 
ON  tags.id = ct.tag_id 
WHERE ct.tag_id <> 18 
     AND NOT EXISTS 
     (
     SELECT NULL 
     FROM company2tag cti 
     WHERE cti.tag_id = 18 
       AND cti.company_id = ct.company_id 
     LIMIT 1, 1 
     ) 
GROUP BY 
     ct.tag_id 
ORDER BY 
     count DESC 

Die Grundidee dabei ist, dass Sie nicht brauchen, zu COUNT(*) : Es genügt zu überprüfen, dass mindestens zwei Werte existieren.

Lesen Sie diesen Artikel in meinem Blog für das ähnliche Problem:

den folgenden Index:

CREATE INDEX ix_company2tag_tag_company_id ON company2tag (tag_id, company_id) 

wird diese Abfrage erheblich verbessern.