2017-08-30 95 views
1

我有一个查询,带出了8,566个结果。但是EXPLAIN显示了一个表格搜索233,190行,我看不出为什么。 它显示了使用“状态”键的问题表(ppi_sar_status),但查询连接到该表上“的LoanID”MySQL JOIN搜索太多行

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

您的桌子上是否有任何索引设置? –

+1

是的,EXPLAIN结果显示可能的键 – swdee

回答

1

这是由于WHERE子句一个不同的充键:

ppi_sar_status.status = 16 

我可能会说很多这样说,但也许你应该尝试使用索引提示。 https://dev.mysql.com/doc/refman/5.7/en/index-hints.html

(但是不要忘了阅读的告诫http://www.mysqldiary.com/the-battle-between-force-index-and-the-query-optimizer/

+0

是的,其中ppi_sar_status.status = 16,但仅用于结果中的贷款,这就是为什么它加入到ppi_sar_status.loanID = ppi_loan.loanID – swdee

+0

@swdee我猜查询优化程序决定首先要做到这一点,因为它认为它会变得更快。您是否遇到此查询的特定性能问题?查询优化器完全可以自由裁剪一个表格,以表达您所需要的结果* *之前*与该表格进行连接,而不是之后;它不能保证它执行任务的顺序。 –

+0

如果删除'ppi_sar_status.status = 16',你会得到多少结果? – ivo

0

清洁您的查询的可读性和张贴的编辑后,我看到了“||”通常与语言中的逻辑OR相关联的组件。但是,在MySQL中,似乎表示串联值等字符串。

在您的WHERE子句中后期...

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')) 

它看起来像你应该有逻辑 “或” 如

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

也..

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

REVISION TO ANSWER

我不知道你的显式索引,但要覆盖索引并帮助进一步优化这个查询,我会建议您在各自的表上使用以下索引。

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* 

但是,现在所有这一切,我还会做更多的事情。 MySQL有一个特殊的关键字“STRAIGHT_JOIN”,告诉MySQL按照我告诉你的顺序查询,不要为我想。正如它声音一样,它试图将你的状态表作为主要状态表。但是既然你真正的驾驶台是贷款的,并且首先在你的FROM子句中,那么这应该是查询的基础。所以我会更改为

SELECT STRAIGHT_JOIN DISTINCT ... rest of query 
+0

结果是一样的 – swdee

+0

[您可以使用||对于MySQL中的逻辑OR](https://dev.mysql.com/doc/refman/5.7/en/logical-operators.html)。我不认为*您可以在MySQL中使用它进行字符串连接,但它是Oracle SQL中的字符串连接运算符。不过,您应该更喜欢使用“OR”作为逻辑OR,因为它更标准。 –

+0

@swdee,查看索引和新特殊关键字的修订答案。 – DRapp