MySQL索引不起作用(特定于用例的场景)

时间:2016-09-19 12:43:10

标签: mysql indexing explain

到目前为止,我的情景如下:

参数由用户控制:(这些参数由仪表板控制,但出于测试目的,我创建了sql参数以便更改其值)

    SET @device_param := "all devices";
    SET @date_param_start_bar_chart := '2016-09-01';
    SET @date_param_end_bar_chart := '2016-09-19';
    SET @country_param := "US";
    SET @channel_param := "all channels";
在后端运行的

查询

SELECT 
  country_code,
  channel_report_tag,
  SUM(count_more_then_30_min_play) AS '>30 minutes',
  SUM(count_15_30_min_play) AS '15-30 Minutes',
  SUM(count_0_15_min_play) AS '0-15 Minutes' 
FROM
  channel_play_times_cleaned 
WHERE IFNULL(country_code, '') = 
  CASE
    WHEN @country_param = "all countries" 
    THEN IFNULL(country_code, '') 
    ELSE @country_param 
  END 
  AND IFNULL(channel_report_tag, '') = 
  CASE
    WHEN @channel_param = "all channels" 
    THEN IFNULL(channel_report_tag, '') 
    ELSE @channel_param 
  END 
  AND iFnull(device_report_tag, '') = 
  CASE
    WHEN @device_param = "all devices" 
    THEN iFnull(device_report_tag, '') 
    ELSE @device_param 
  END 
  AND playing_date BETWEEN @date_param_start_bar_chart 
  AND @date_param_end_bar_chart 
GROUP BY channel_report_tag 
ORDER BY SUM(count_more_then_30_min_play) DESC 
limit 10 ;

我申请的索引

CREATE INDEX my_index 
ON channel_play_times_cleaned (
  country_code,
  channel_report_tag,
  device_report_tag,
  playing_date,
  channel_report_tag
)

我已关注此链接:My SQL Index Cook-Book Guide以创建我的索引。

但是,执行上述查询时 EXPLAIN 关键字告诉我没有使用索引。

enter image description here

我想在这里做错什么?

3 个答案:

答案 0 :(得分:2)

  1. 您在前3个where条件中使用函数和case表达式。简单的字段索引不能用于加速这种查找。

  2. MySQL可能会使用playing_date条件的索引,但该字段不是引用索引中最左侧的字段,因此引用的索引也不适合。

  3. 如果我是你,我会从where条件中删除逻辑,并通过构造这样一个已解决案例条件并仅发出必要的sql的sql语句将其移入应用程序层。

答案 1 :(得分:1)

CASE子句中的WHERE个表达式正在强制进行全表扫描。显然,他们必须......但是如何?

你必须像优化器一样思考并记住它的工作是尽可能避免工作。

考虑这个问题:

SELECT * FROM users
 WHERE first_name LIKE '%a%';

必须读取每一行才能找到包含字母“a”的所有first_name值。很慢。

现在,这一个:

SELECT * FROM users
 WHERE first_name LIKE '%a%'
   AND 2 < 1;

对于每一行,您要求服务器再次检查first_name,并且仅包括其中2小于1的行。

是慢还是快?

速度非常快,因为优化器检测到Impossible WHERE。扫描行没有意义,因为2&lt; 1总是假的。

现在,使用此逻辑告诉优化器您真正想要的是什么:

不是这个:

  WHERE IFNULL(country_code, '') = 
   CASE
     WHEN @country_param = "all countries" 
     THEN IFNULL(country_code, '') 
     ELSE @country_param 
   END 
  AND

但是这个:

 WHERE
  (
    (
      @country_param = "all countries" 
    )
    OR
    (
      @country_param != "all countries"
      AND
      country_code = @country_param
    )
  )
  AND ...

差异应该是明显的。如果@country_param =“所有国家/地区”,则不需要第二次测试,否则,只需要具有匹配国家/地区的行,并且WHERE子句的这一部分对于所有其他行定义为false,从而允许索引在country_param上使用。

这些OR'ed表达式中的一个或另一个总是为false,并且那个将在早期进行优化 - 从不对每一行进行评估。表达式@country_param != "all countries"的处理方式与表达式2 < 12 > 1没有区别。它不会根据行中的数据改变其真实性,因此它只需要在开始时进行一次评估。

重复其他CASE。您几乎不应该将列作为参数传递给WHERE子句中的函数,因为优化器不能“向后查看”函数并形成智能查询计划。

答案 2 :(得分:0)

其他答案解释了为什么您的查询很慢。我将解释你应该做什么。

编写代码以“构造”查询。如果用户说“所有国家/地区”,则会遗漏country_code的测试,或者添加AND country_code = "US"。没有@variables,没有CASE等等。

然后,除少数情况外,一个5列索引不起作用。相反,要了解用户要求的内容,然后构建一些2列索引以涵盖流行的案例。