到目前为止,我的情景如下:
参数由用户控制:(这些参数由仪表板控制,但出于测试目的,我创建了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 关键字告诉我没有使用索引。
我想在这里做错什么?
答案 0 :(得分:2)
您在前3个where条件中使用函数和case表达式。简单的字段索引不能用于加速这种查找。
MySQL可能会使用playing_date
条件的索引,但该字段不是引用索引中最左侧的字段,因此引用的索引也不适合。
如果我是你,我会从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 < 1
或2 > 1
没有区别。它不会根据行中的数据改变其真实性,因此它只需要在开始时进行一次评估。
重复其他CASE
。您几乎不应该将列作为参数传递给WHERE
子句中的函数,因为优化器不能“向后查看”函数并形成智能查询计划。
答案 2 :(得分:0)
其他答案解释了为什么您的查询很慢。我将解释你应该做什么。
编写代码以“构造”查询。如果用户说“所有国家/地区”,则会遗漏country_code
的测试,或者添加AND country_code = "US"
。没有@variables,没有CASE等等。
然后,除少数情况外,一个5列索引不起作用。相反,要了解用户要求的内容,然后构建一些2列索引以涵盖流行的案例。