简体   繁体   中英

MySQL query is very slow when adding JOIN into WHERE

The following query runs quickly except when I add in the 'areacode.hours = 2'. Instead of taking a second it now takes close to a minute.

SELECT lead.id, first, last, lead.phone, valid_phone, mobile_phone 
FROM lead 
LEFT JOIN dnt ON dnt.phone = lead.phone
LEFT JOIN areacode ON areacode.code = LEFT(lead.phone, 3)
LEFT JOIN campaign ON campaign.id = lead.campaign_id
WHERE dnt.id IS NULL 
AND campaign.lft BETWEEN 0 AND 1000 
AND lead.datetime BETWEEN '2017-06-01 00:00:00' AND '2017-06-01 23:59:59' 
AND areacode.hours = 2
GROUP BY lead.phone
ORDER BY lead.phone DESC
LIMIT 1,1000

Results of EXPLAIN

1   SIMPLE  lead    index   campaign_id,datetime    phone   13  NULL    181181  Using where
1   SIMPLE  campaign    eq_ref  PRIMARY PRIMARY 4   data3_db.lead.campaign_id   1   Using where
1   SIMPLE  areacode    ref code,hours  code    11  func    1   Using index condition; Using where
1   SIMPLE  dnt ref phone   phone   12  data3_db.lead.phone 1   Using where; Using index; Not exists

Your where clause limits are being executed after the joins complete. This will result in your left joins acting like INNER joins. I would recommend moving the limits for those tables on the right side of a left join to the joins themselves; so the limits are applied before the join occurs. (or as part of it)

Exception would be if you're checking for Null on a field; That could remain in the where clause. but you have no way to distinguish between dnt.ids which are are null in the table, and those which are null as part of the left join if you put it in the where clause. However, given this is an "ID" field I'll assume it can't be null and leaving it in the where makes sense.

At times the compiler may attempt to apply the filter after the join when you have thousands of records * 4 tables you record growth could be large; by moving the limits to occur before/on join, the engine can reduce the temporary datset it has to build before it filters out the records. On Inner joins this doesn't matter but on outer joins it can not only effect performance; but desired results.

SELECT lead.id, first, last, lead.phone, valid_phone, mobile_phone 
FROM lead 
LEFT JOIN dnt 
  ON dnt.phone = lead.phone
LEFT JOIN areacode 
  ON areacode.code = LEFT(lead.phone, 3)
 AND areacode.hours = 2
LEFT JOIN campaign 
  ON campaign.id = lead.campaign_id
 AND campaign.lft BETWEEN 0 AND 1000 
WHERE lead.datetime BETWEEN '2017-06-01 00:00:00' AND '2017-06-01 23:59:59' 
  AND dnt.id IS NULL 
GROUP BY lead.phone
ORDER BY lead.phone DESC
LIMIT 1,1000

If that still doesn't produce the desired results and time, then i'd look at desired indexes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM