使用多个条件连接加快 MySql 查询时间

Speed up MySql query time with multiple conditional joins

有 3 个 tables,persontbl1,persontbl2(每行 7500 行)和时间表(约 3000 个活动时间表,即 schedule.status = 0)。 Person tables 包含与一对一关系相同的人的数据,两者之间的 INNER join 需要不到一秒钟的时间。时间表 table 包含有关要采访的人的数据,并非所有人都有时间表 table。使用 Left join 查询立即需要大约 45 秒,这会导致各种问题。

SELECT persontbl1._CREATION_DATE, persontbl2._TOP_LEVEL_AURI, 
       persontbl2.RESP_CNIC, persontbl2.RESP_CNIC_NAME, 
       persontbl1.MOB_NUMBER1, persontbl1.MOB_NUMBER2, 
       schedule.id, schedule.call_datetime, schedule.enum_id,
       schedule.enum_change, schedule.status
FROM persontbl1
INNER JOIN persontbl2 ON (persontbl2._TOP_LEVEL_AURI = persontbl1._URI)
                      AND (AGR_CONTACT=1)
LEFT JOIN SCHEDULE ON (schedule.survey_id = persontbl1._URI)
                  AND (SCHEDULE.status=0)
                  AND (DATE(SCHEDULE.call_datetime) <= CURDATE())   
ORDER BY schedule.call_datetime IS NULL DESC, persontbl1._CREATION_DATE ASC

查询说明如下:

时间表Table结构:

计划 Table 索引:

如果需要任何进一步的信息,请告诉我。

谢谢。

编辑:添加了完全限定的 table 名称及其列。

我猜 AGR_CONTACT 来自 p1。这是您要优化的查询:

SELECT p1._CREATION_DATE, _TOP_LEVEL_AURI, RESP_CNIC, RESP_CNIC_NAME,
       MOB_NUMBER1, MOB_NUMBER2, 
       s.id, s.call_datetime, s.enum_id, s.enum_change, s.status
FROM persontbl1 p1 INNER JOIN
     persontbl2 p2
     ON (p2._TOP_LEVEL_AURI = p1._URI) AND (p1.AGR_CONTACT = 1) LEFT JOIN
     SCHEDULE s
     ON (s.survey_id = p1._URI) AND
     (s.status = 0) AND
     (DATE(s.call_datetime) <= CURDATE())   
ORDER BY s.call_datetime IS NULL DESC, p1._CREATION_DATE ASC;

此查询的最佳索引是:persontbl2(agr_contact)persontbl1(_TOP_LEVEL_AURI, _uri)schedule(survey_id, status, call_datime)

不建议在日期时间前后使用 date()。通常,这排除了索引的使用。但是,在这种情况下,您有一个 left join,因此没有什么区别。无论如何,该列不用于过滤。 schedule 上的索引仅用于覆盖 on 子句。

你应该只替换这一行:

AND (DATE(SCHEDULE.call_datetime) <= CURDATE())

给这个:

AND SCHEDULE.call_datetime <= '2015-04-18 00:00:00'

所以 mysql 不会为每条记录调用 2 个函数,但会使用静态常量 '2015-04-18 00:00:00'.

因此,如果您的查询是:

,您可以尝试提高性能
SELECT persontbl1._CREATION_DATE, persontbl2._TOP_LEVEL_AURI, 
       persontbl2.RESP_CNIC, persontbl2.RESP_CNIC_NAME, 
       persontbl1.MOB_NUMBER1, persontbl1.MOB_NUMBER2, 
       schedule.id, schedule.call_datetime, schedule.enum_id,
       schedule.enum_change, schedule.status
FROM persontbl1
INNER JOIN persontbl2 ON (persontbl2._TOP_LEVEL_AURI = persontbl1._URI)
                      AND (AGR_CONTACT=1)
LEFT JOIN SCHEDULE ON (schedule.survey_id = persontbl1._URI)
                  AND (SCHEDULE.status=0)
                  AND (SCHEDULE.call_datetime <= '2015-02-01 00:00:00')   
ORDER BY schedule.call_datetime IS NULL DESC, persontbl1._CREATION_DATE ASC

编辑 1 所以你说没有 LEFT JOIN 部分已经足够快了,所以你可以试试:

SELECT persontbl1._CREATION_DATE, persontbl2._TOP_LEVEL_AURI, 
       persontbl2.RESP_CNIC, persontbl2.RESP_CNIC_NAME, 
       persontbl1.MOB_NUMBER1, persontbl1.MOB_NUMBER2, 
       s.id, s.call_datetime, s.enum_id,
       s.enum_change, s.status
FROM persontbl1
INNER JOIN persontbl2 ON (persontbl2._TOP_LEVEL_AURI = persontbl1._URI)
                      AND (AGR_CONTACT=1)
LEFT JOIN 
  (SELECT *
   FROM SCHEDULE
   WHERE status=0
     AND call_datetime <= '2015-02-01 00:00:00'  
  ) s
ON s.survey_id = persontbl1._URI

ORDER BY s.call_datetime IS NULL DESC, persontbl1._CREATION_DATE ASC