2017-08-30 1 views
1

Ich habe die eine Abfrage, die 8.566 Ergebnisse hervorbringt. Aber das EXPLAIN zeigt eine Tabelle, die 233.190 Zeilen durchsucht und ich kann nicht sehen warum. Es zeigt die Problemtabelle (ppi_sar_status), um den ‚Status‘ Schlüssel, aber die Abfrage verknüpft auf diese Tabelle auf einem Differnt Schlüssel ‚loanID‘MySQL JOIN Suche zu viele Zeilen

View the EXPLAIN results

SELECT DISTINCT 
     ppi_loan.loanID, 
     ppi_loan.lender, 
     ppi_loan.customerID, 
     ppi_loan.agreementNo, 
     loan_number, 
     ppi_lenders.name, 
     ppi_status.status, 
     ppi_statuses.description, 
     ppi_loan.broker, 
     (SELECT sarSent 
      FROM ppi_sar 
      WHERE ppi_sar.customerID = ppi_loan.customerID 
       AND ppi_sar.lender = ppi_loan.lender 
      ORDER BY sarSent DESC 
      LIMIT 1) as sarSent, 
     (SELECT COUNT(DISTINCT(groupID)) 
      FROM ppi_mdrs 
      WHERE ppi_mdrs.customerIDfk = ppi_loan.customerID 
       AND ppi_mdrs.lender = ppi_loan.lender 
       AND sent_to_lender = '0000-00-00 00:00:00') AS mdrs_sent, 
     (SELECT sent 
      FROM ppi_mdrs 
      WHERE ppi_mdrs.customerIDfk = ppi_loan.customerID 
       AND ppi_mdrs.lender = ppi_loan.lender 
      ORDER BY sent DESC 
      LIMIT 1) AS mdr_last_sent, 
     mobilePhone, 
     homePhone, 
     title, 
     firstName, 
     lastName, 
     loaSent 
    FROM 
     ppi_loan 
     JOIN ppi_sar_status 
      ON ppi_loan.loanID = ppi_sar_status.loanID 
     JOIN ppi_customer 
      ON ppi_loan.customerID = ppi_customer.customerID 
     JOIN ppi_lenders 
      ON ppi_loan.lender = ppi_lenders.id 
     JOIN ppi_status 
      ON ppi_loan.customerID = ppi_status.customerID 
      JOIN ppi_statuses 
       ON ppi_status.status = ppi_statuses.status 
     LEFT JOIN ppi_mdrs 
      ON ppi_loan.customerID = customerIDfk 
      AND ppi_loan.lender = ppi_mdrs.lender 
    WHERE 
      ppi_loan.customerID != 10 
     AND ppi_status.status != 9 
     AND ppi_status.status != 32 
     AND ppi_status.status != 54 
     AND ppi_status.status != 58 
     AND ppi_status.status != 59 
     AND ppi_status.status != 61 
     AND ppi_status.status != 69 
     AND ppi_status.status != 60 
     AND ppi_status.history = 0 
     AND ppi_loan.customerID 
     IN (SELECT customerID 
       FROM ppi_status 
       WHERE (status = 5 || status = 6 || status = 79) 
        AND timestamp > '2015-04-01' 
        AND ppi_status.customerID = ppi_loan.customerID) 
     AND ppi_sar_status.status = 16 
     AND ppi_sar_status.history = 0 
     AND (cc_type = '' || (cc_type != '' AND cc_accepted = 'no')) 
     AND ppi_loan.deleted = 'no' 
+0

Haben Sie irgendwelche Indizes auf Ihren Tischen eingerichtet? –

+1

Ja, die EXPLAIN-Ergebnisse zeigen mögliche Schlüssel – swdee

Antwort

1

Es ist aufgrund der Klausel WHERE:

Ich werde wahrscheinlich eine Menge Flack für das sagen, aber vielleicht sollten Sie versuchen, einen Index Hinweis zu verwenden. https://dev.mysql.com/doc/refman/5.7/en/index-hints.html

(Aber nicht zu vergessen die Einsprüche http://www.mysqldiary.com/the-battle-between-force-index-and-the-query-optimizer/ zu lesen)

+0

Ja wo ppi_sar_status.status = 16, aber nur für Kredite in den Ergebnissen, deshalb ist es bei ppi_sar_status.loanID = ppi_loan.loanID – swdee

+0

@swdee Ich denke, der Abfrageoptimierer entschieden um dieses Bit zuerst zu machen, weil es dachte, es würde schneller werden. Haben Sie ein bestimmtes Leistungsproblem mit dieser Abfrage? Ein Abfrageoptimierer ist vollkommen in der Lage, eine Tabelle auf die Ergebnisse zu reduzieren, die Sie * benötigt * haben, bevor Sie eine Verbindung zu dieser Tabelle und nicht erst danach herstellen. Die Reihenfolge, in der die Dinge gemacht werden, ist nicht garantiert. –

+0

Wenn Sie 'ppi_sar_status.status = 16 'entfernen, wie viele Ergebnisse erhalten Sie? – ivo

0

Nach dem Reinigen der Lesbarkeit Ihrer Abfrage und Buchung des bearbeiten, sah ich die „||“ Komponenten, die typischerweise in Sprachen einem logischen ODER zugeordnet ist. In MySQL scheint es jedoch die Verkettung von Werten wie Strings darzustellen.

Im späteren Teil Ihrer WHERE-Klausel ...

WHERE (status = 5 || status = 6 || status = 79) 
        AND timestamp > '2015-04-01' 
        AND ppi_status.customerID = ppi_loan.customerID) 
     AND ppi_sar_status.status = 16 
     AND ppi_sar_status.history = 0 
     AND (cc_type = '' || (cc_type != '' AND cc_accepted = 'no')) 

es sieht aus wie Sie logische haben sollte "oder" wie

WHERE (status = 5 OR status = 6 OR status = 79) 

und auch ..

 AND (cc_type = '' OR (cc_type != '' AND cc_accepted = 'no')) 

ÜBERARBEITUNG ZUR ANTWORT

Ich kenne Ihre expliziten Indizes nicht, aber um überdeckende Indizes zu erhalten und diese Abfrage weiter zu optimieren, würde ich die folgenden Indizes für Ihre jeweiligen Tabellen vorschlagen.

table   index 
ppi_sar   (customerID, lender) *or lender,customerid either way* 
ppi_mdrs  (customerIdfk, lender, sent_to_lender) *or lender,customerid,sent...* 
ppi_sar_status (loanid, status, history) 
ppi_status  (customerID, status, history, timestamp) *timestamp optional* 

Aber jetzt, mit all dem, würde ich auch noch eine Sache tun. MySQL hat ein spezielles Schlüsselwort "STRAIGHT_JOIN", das MySQL anweist, in der Reihenfolge, die ich Ihnen gesagt habe, abzufragen. Denken Sie nicht an mich. Während es klingelt, versucht es Ihre Status-Tabelle als primäre. Aber da Ihr wahrer treibender Tisch der von Krediten ist, und zuerst in Ihrer FROM-Klausel, sollte dies die Basis der Abfrage sein. also würde ich zu

wechseln
SELECT STRAIGHT_JOIN DISTINCT ... rest of query 
+0

Die Ergebnisse sind die gleichen, – swdee

+0

[Sie können || verwenden für logisches OR in MySQL] (https://dev.mysql.com/doc/refman/5.7/en/logical-operators.html). Ich denke nicht * Sie können es für String-Verkettung in MySQL verwenden, obwohl es ein String-Verkettungsoperator in Oracle SQL ist. Nichtsdestotrotz sollten Sie wahrscheinlich "ODER" für logisches ODER bevorzugen, da es Standard ist. –

+0

@swdee, siehe überarbeitete Antwort für Indizes und neues spezielles Schlüsselwort. – DRapp