我将运行像
这样的查询的表上的正确索引是什么SELECT * FROM table WHERE NOW() BETWEEN date1_col AND date2_col
date1_col
上的索引是否足够,或者我必须索引两列,还是需要复杂索引?
此外,每行会计算NOW()
吗?在构造查询时将它作为文字提供会更好吗?
答案 0 :(得分:1)
让我们添加4个索引并使用EXPLAIN
:
EXPLAIN FORMAT=JSON SELECT *
FROM user
WHERE NOW() BETWEEN date_1 AND date_2;
并查看结果:
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "user",
"access_type": "range",
"possible_keys": [
"idx_date_1_date_2",
"idx_date_1",
"idx_date_2",
"idx_date_2_date_1"
],
"key": "idx_date_2",
"used_key_parts": [
"date_2"
],
"key_length": "5",
"rows": 1,
"filtered": 100,
"attached_condition": "(<cache>(now()) between `portalintl_db`.`user`.`date_1` and `portalintl_db`.`user`.`date_2`)"
}
}
}
首先,now()
标有<cache>
标记。这意味着NOW()
只会计算一次。
第二,idx_date_2
已用于查询。因此,date_2
列只需要一个索引。此外,idx_date_2_date_1
也可以,但MySQL只使用date_2
部分。
如果想了解有关EXPLAIN
输出的更多详细信息,请随时阅读文档:http://dev.mysql.com/doc/refman/5.7/en/explain-extended.html
答案 1 :(得分:1)
BETWEEN date_1 AND date_2
无法优化。但是,优化器将在运行时选择它们之间:
INDEX(date_1, date_2) if the date is near the end of the date_1 values, or
INDEX(date_2, date_1) if the date is near the start of that index.
所以,拥有这两个索引。在最坏的情况下,查询将不得不扫描近一半的表。
那么,为什么不使用1列版本呢?让我们再解剖一下......索引是根据指定的列排序的B +树。这样的树可以有效地线性扫描(从开始到日期或从日期到结束,取决于哪个索引),但在此之后,它需要检查date_1 / 2中的另一个。
如果您只有INDEX(date_1)
,那么它必须进入数据所在的BTree,以便date_2
进行检查。
另一方面,如果您使用的是INDEX(date_1, date_2)
,那么该值就在那里,并且可以更快地测试“附加条件”。在非JSON EXPLAIN
中,由Extra列中的“Using index condition”表示。这是“ICP”。
我认为(但不确定)仅使用 2列索引会更好。
注意:我认为ICP在5.6.10中添加了。所以这取决于你的版本的年龄。
“覆盖”索引是包含SELECT
中提到的所有列的索引。这甚至更好,但它有些限制,因为将SELECT user
更改为SELECT user, foo
会使这些不再“覆盖”:
INDEX(date_1, date_2, user)
INDEX(date_2, date_1, user)