2

我在 mysql 连接方面的知识陷入了僵局,而且我尝试执行的查询花费的时间太长......虽然我自己学习 mysql 的时间很短,但我已经花时间阅读关于索引和连接的机制,做了很多谷歌搜索并尝试了几种不同的查询格式。无济于事,我需要帮助。

首先,我会说我的数据库目前正在针对选择查询的速度进行优化。我知道我有太多的索引......我学习mysql的理论是制作一些太多的索引并检查mysql优化器为我的目的选择什么(通过使用解释确定),然后确定它为什么选择所说的索引.

无论如何,我有四个表:table1,table2,table3,table4 ...

table1.ID1 是主键,table1 中的其他数据可能会分成 table2 中的多个内容。table2.ID1 标识 table1 中基于内容表单 table1 table2 构建的每个条目。ID2 是 table2 table3 的主键。ID2 标识 table3 中基于内容表单 table2 table3 构建的每个条目。ID3 是 table3 table4 的主键。 ID3 标识 table4 中基于内容表单 table3 构建的每个条目

并非table1中的每个条目在table2中都有对应的数据,同样table2到table3,table3到table4。

我需要做的是检索出现在日期范围内的 ID2 的不同值,并且仅当 table2 内容最终出现在 table4 中时。我面临的挑战是只有 table1 有一个日期列,我只需要也出现在 table4 中的条目。

以下查询大约需要 2 分钟。

select table2.ID2 from table1 
left join table2 on
table1.ID1 = table2.ID1
left join table3 on
table3.ID2 = table2.ID2 
left join table4 on
table4.ID3 = table3.ID3
where table1.Date between "2012-03-11" and "2012-03-18

通过对上述查询使用解释,我认为没有理由需要这么长时间。

+----+-------------+--------------+-------+----------------------+----------+---------+------------------------------+-------+--------------------------+
| id | select_type | table        | type  | possible_keys        | key      | key_len | ref                          | rows  | Extra                    |
+----+-------------+--------------+-------+----------------------+----------+---------+------------------------------+-------+--------------------------+
|  1 | SIMPLE      | table1       | range | ...                  | Datekey  | 9       | NULL                         | 17528 | Using where; Using index |
|  1 | SIMPLE      | table2       | ref   | ...                  | ID1key   | 8       | mydata.table1.POSTID         |     1 |                          |
|  1 | SIMPLE      | table3       | ref   | ...                  | ID2key   | 8       | mydata.table2.SrcID          |    20 |                          |
|  1 | SIMPLE      | table4       | ref   | ...                  | ID3key   | 8       | mydata.table3.ParsedID       |    10 | Using index              |
+----+-------------+--------------+-------+----------------------+----------+---------+------------------------------+-------+--------------------------+

我已经用“...”替换了可能键的名称,因为它并不那么重要。在任何情况下,都会选择一个键。

此外,查询结果集中的行数比解释结果集中声称的匹配 17528 行要多得多。怎么可能更多??

我究竟做错了什么?我也尝试过内部连接,但没有运气。我解释我的查询的方式是一个 4 向维恩图,具有重叠标准的行数很少,并通过日期范围上的索引进一步优化。

如果我添加'distinct(table2.ID2)',我至少会得到我想要的结果集,但为什么我得到的结果集比我预期的要长得多,为什么要花这么长时间?

抱歉,如果我的问题的任何部分模棱两可,我很乐意根据需要澄清。

谢谢,布赖恩

编辑:

所有索引都引用 BIGINT 列,因为我希望我的数据库变得相当大并且需要相当多的唯一行标识符......也许 bigint 是矫枉过正并且减小该列的大小和/或索引会进一步加快速度.

这是我的最终解决方案,基于以下接受的答案:

select ID2 from table2
where exists
    (select 1 from table1 r
    where table1.Date between "2012-03-11" and "2012-03-18" and table2.ID1 = table1.ID1
    )
and exists
    (select 1 from table3
    where exists 
        (select 1 from table4 where table4.ID3 = table3.ID3) 
    )

此外,我意识到我缺少一个多字段索引,关联 table2.ID1 和 table2.ID2... 添加此索引后,此语句在大约 11 秒内返回,并返回大约 20,000 行。

考虑到我的每个表中的行数,我认为这是合理的 table1: ~480,000 table2: ~480,000 table3: ~6,000,000 table4: ~60,000,000

这听起来有效吗?在我确认这是我应该期待的最佳表现后,我会接受答案。我在 Xeon 3GHz 系统上运行 3gb 内存、ubuntu 12.04、mysql 5.5.24

4

1 回答 1

2

很可能,您的表之间有多个匹配项。假设 table1 匹配 table2 中的 5 行和 table3 中的 10 行。然后你最终在输出中有 50 行。

所以解决这个问题,您需要将连接限制为每个表一行。

一种方法是使用 in 子句。如果您使用连接进行过滤,那么您可以使用 where 子句:

where table2.id1 in (select table1.id1 from table1)

“in”可以防止重复。

另一种选择是通过连接来预先聚合连接中的查询。

从优化的角度来看,Mysql 似乎更喜欢 where 子句的稍微不同的结构:

where exists (select 1 from table1 where table1.id = table2.id)
于 2012-07-29T02:36:33.243 回答