适用于子句之间的索引

时间:2017-01-17 14:20:30

标签: mysql indexing

我将运行像

这样的查询的表上的正确索引是什么
SELECT * FROM table WHERE NOW() BETWEEN date1_col AND date2_col

date1_col上的索引是否足够,或者我必须索引两列,还是需要复杂索引?

此外,每行会计算NOW()吗?在构造查询时将它作为文字提供会更好吗?

2 个答案:

答案 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)