BETWEEN查询性能不佳

时间:2013-09-25 14:51:45

标签: mysql sql performance indexing between

我正在尝试使用此查询在多个时段之间查找个人的考试结果:

SELECT * FROM RESULTS AS R, Define_Times AS T 
WHERE R.PERSONID = T.PERSONID AND ( 
(R.DATE BETWEEN T.Previous_Month_Start AND T.Previous_Month_End) OR 
(R.DATE BETWEEN T.Next_Month_Start AND T.Next_Month_End) OR 
(R.DATE BETWEEN T.Six_Month_Start AND T.Six_Month_End) OR 
(R.DATE BETWEEN T.One_Year_Start AND T.One_Year_End) OR 
(R.DATE BETWEEN T.Two_Year_Start AND T.Two_Year_End) OR 
(R.DATE BETWEEN T.Three_Year_Start AND T.Three_Year_End) OR 
(R.DATE BETWEEN T.Four_Year_Start AND T.Four_Year_End) )

上一个/下一个/ One_Year等对每个人都不同。

解释给出:

| id | select_type | table | type | possible_keys | key  | key_len | ref             | rows  | Extra       |
|  1 | SIMPLE      | T     | ALL  | PEOPLE        | NULL | NULL    | NULL            | 75775 |             |
|  1 | SIMPLE      | R     | ref  | IDX3,IDX2     | IDX3 | 5       | T.PERSONID      |  3550 | Using where |

“结果”表有大约3亿行。 Define_Times有75,000。

它正在服用AGES。

我看到第一种类型是ALL,这很糟糕。但如果它如此糟糕,为什么它不使用PERSONID上的索引(称为PEOPLE),它被识别为可能?我该怎么做才能改善这一点?

我也看不到使用日期索引 - 在R.DATE有一个。 (这是索引中名为IDX2的序列中的第一个。)

对不起打字错误 - 我的键盘坏了,提前谢谢。

2 个答案:

答案 0 :(得分:2)

问题在于您将ORed组合在一起的所有条件。

如果可能,重构数据库,以便Define_Time只有四列:

 CREATE TABLE Define_Times (
    PersonID INTEGER,
    PeriodType SomeType,
    StartDate DATE,
    EndDate DATE )

然后,每个人获得7条记录(或者更多,如果你的例子中有更多的时期没有搜索),其中PeriodType表示日期指定的时间段(你可以使用PM,NM,SM等文本值) ,1Y,2Y,3Y,4Y或者您可以使用指向另一个表中描述的整数值。)

然后,像这样重写你的查询:

SELECT * FROM RESULTS AS R, Define_Times AS T 
WHERE R.PERSONID = T.PERSONID 
   AND R.DATE BETWEEN T.StartDate AND T.EndDate
   AND T.PeriodType IN (PM,NM,SM,1Y,2Y,3Y,4Y)

此查询至少可优化

此查询将为每个人生成每个匹配期间的一条记录。如果你的期间不重叠,那很好(只有一个匹配的记录)。如果您的句点重叠并且您只需要每个结果集一个记录,则需要通过聚合结果集中的记录来对DISTINCT或GROUP BY进行一些额外的工作。

另请注意,如果在Define_Times表中有任何其他句点,则可以删除WHERE子句的AND T.PeriodType部分。

答案 1 :(得分:0)

作为比较,您可以运行此等效查询

SELECT * FROM Define_Times AS T 
INNER JOIN RESULTS AS R on
(R.PERSONID = T.PERSONID and 
  ( 
  (R.DATE BETWEEN T.Previous_Month_Start AND T.Previous_Month_End) OR 
  (R.DATE BETWEEN T.Next_Month_Start AND T.Next_Month_End) OR 
  (R.DATE BETWEEN T.Six_Month_Start AND T.Six_Month_End) OR 
  (R.DATE BETWEEN T.One_Year_Start AND T.One_Year_End) OR 
  (R.DATE BETWEEN T.Two_Year_Start AND T.Two_Year_End) OR 
  (R.DATE BETWEEN T.Three_Year_Start AND T.Three_Year_End) OR 
  (R.DATE BETWEEN T.Four_Year_Start AND T.Four_Year_End) 
  ) 
)

我已经看到优化器在这种形式下有时会更好地工作。

此外,既然你是OR表达式之间的所有日期,它几乎无法使用日期索引,因为任何日期范围都可以满足where子句。

编辑 - 增加

如果您不想运行查询,请至少尝试比较估计的执行计划