我正在构建一个星型模式,作为我正在构建的分析应用程序的后端。我的查询生成器使用常规的星形连接模式构建查询。下面是一个示例查询,其中事实表连接到两个维度表,维度表按最终用户选择的常量值进行过滤。
我使用的是MySQL 5.5,所有表都是MyISAM。
在这个问题中,我只是想拉出前N行(在这种情况下,前1行)
EXPLAIN
SELECT fact_table.*
FROM
fact_table
INNER JOIN
dim1 ON (fact_table.dim1_key = dim1.pkey)
INNER JOIN
dim2 ON (fact_table.dim2_key = dim2.pkey)
WHERE
dim1.constant_value = 123
AND dim2.constant_value = 456
ORDER BY
measure1 ASC LIMIT 1
解释输出如下。维度键都解析为常量值,因为有一个唯一键应用于它们的值。
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dim1
type: const
possible_keys: PRIMARY,dim1_uk
key: dim1_uk
key_len: 8
ref: const
rows: 1
Extra: Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: dim2
type: const
possible_keys: PRIMARY,dim2_uk
key: dim2_uk
key_len: 8
ref: const
rows: 1
Extra:
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: fact_table
type: ref
possible_keys: my_idx
key: my_idx
key_len: 16
ref: const,const
rows: 50010
Extra: Using where
这是事实表上的索引:
show indexes from fact_table
*************************** 10. row ***************************
Table: fact_table
Non_unique: 1
Key_name: my_idx
Seq_in_index: 1
Column_name: dim1_key
Collation: A
Cardinality: 24
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 11. row ***************************
Table: fact_table
Non_unique: 1
Key_name: my_idx
Seq_in_index: 2
Column_name: dim2_key
Collation: A
Cardinality: 70
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 12. row ***************************
Table: fact_table
Non_unique: 1
Key_name: my_idx
Seq_in_index: 3
Column_name: measure1
Collation: A
Cardinality: 5643
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
在分析此查询时,我看到查询花费大部分时间执行文件排序操作“排序结果”。我的问题是,即使使用正确的索引,为什么这个查询不能简单地拉出第一个值而不进行排序? my_idx已经在右列上排序,并且索引中首先出现的两列解析为常量,如计划中所示。
如果我重写查询,如下所示,我可以获得我想要的计划,没有文件排序。
SELECT fact_table.*
FROM
fact_table
WHERE
dim1_key = (select pkey from dim1 where constant_value = 123)
AND dim2_key = (select pkey from dim2 where constant_value = 456)
ORDER BY
measure1 ASC LIMIT 1
更改生成这些SQL命令的工具会很昂贵,所以即使查询是以原始格式编写的,我也希望避免使用此文件。
我的问题是,即使索引上的第一个键是常量(通过INNER JOIN)并且索引按正确顺序排序,为什么MySQL选择执行文件排序?有办法解决这个问题吗?
答案 0 :(得分:0)
我的问题是,即使索引上的第一个键是常量(通过INNER JOIN)并且索引按正确顺序排序,为什么MySQL选择执行文件排序?有办法解决这个问题吗?
因为结果集的顺序取决于用于读取JOIN中第一个表的索引,但正如您在EXPLAIN中看到的那样,JOIN实际上是从dim1
表开始的。
这可能看起来很奇怪,但要隐式强制MySQL从fact_table
开始,你需要将维度表中的索引更改为(pkey,constantvalue)而不是(constantvalue),否则MySQL优化器将以条件constantvalue=some_value
返回最小行的表。问题是您可能需要这些索引用于其他查询。
相反,您可以尝试将STRAIGHT_JOIN选项添加到SELECT并明确强制执行该命令。