【问题标题】:Does a composite INDEX work for JOINING (MANY to MANY)?复合索引是否适用于加入(多对多)?
【发布时间】:2020-05-19 17:47:02
【问题描述】:

tt - 是一个映射表。 tt结构是

table_1 |表_2 | table_3

   SELECT t1.foo1, t2.foo2, t3.foo3 FROM tt 
        JOIN table1 t1 ON tt.table_1 = t1.id
        JOIN table2 t2 ON tt.table_2 = t2.id
        JOIN table3 t3 ON tt.table_3 = t3.id
    WHERE t2.value = 'test'

索引是否有意义并且将为tt 工作? 综合索引为ix_table_1__table_2__table_3(table1, table2, table_3)

如果这样做 - 为什么,如果不这样做 - 为什么?

【问题讨论】:

  • 永远不要使用邪恶的SELECT *
  • 演示会翻倍

标签: mysql sql join indexing query-optimization


【解决方案1】:

(我不同意另一个答案中推荐的索引。)

SELECT * FROM tt 
    JOIN table1 t1 ON tt.table_1 = t1.id
    JOIN table2 t2 ON tt.table_2 = t2.id
    JOIN table3 t3 ON tt.table_3 = t3.id
WHERE t2.value = 'test'

当优化器选择如何执行JOIN 时,它通常是这样工作的:

  1. 从具有最佳WHERE 的表开始。这将是t2。所以需要有一个INDEX value 开头
  2. 然后转到另一张桌子。唯一的下一个选择是tt,因为这次是ON 子句。
  3. 之后是t1t3,顺序不限。

现在是索引,按上面的顺序列出:

t2:  INDEX(value)
tt:  INDEX(table_2)

t1t3 可以通过它们的id 访问。因此,假设您遵循id 作为PK 的约定,那么PRIMARY KEY(id) 已经存在。

现在让我们切换到新版本的查询:

SELECT t1.foo1, t2.foo2, t3.foo3 FROM tt  ...

这样,我们可以制作更好的索引。 “覆盖”索引是一个INDEX,它包括查询中任何地方所需的所有列。所以,让我们添加任何此类列:

t2:  INDEX(value, id, foo2)
tt:  INDEX(table_2, table_3, table_1)  -- table_2 must be first

在考虑“覆盖”索引时需要注意两点:

  • 通过PRIMARY KEY 进入表时,创建“覆盖”索引没有任何优势。 PK 与数据“聚集”在一起,因此有效地“覆盖”。
  • 在索引中包含太多列是不“明智的”。您要求所有 (*) 列。

有关创建最佳索引的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql

唉,EXPLAIN 显示了用可用的东西做了什么;它不应该添加什么索引,也没有其他提示。

您的表格看起来不像传统的多:多表格。有关该类型表的具体提示,请参阅此:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table

FOREIGN KEYs:FK 提供 (1) 约束(用于数据完整性)和 (2) INDEX 以提高检查约束的效率。当您同时创建 FK INDEX 时,MySQL 可能 足够聪明,可以避免只有两个索引就足够了。 INDEX(table_2) 是 FK 所需的全部,但 INDEX(table_2, table_3, table_1) 将为该 FK “工作”。拥有两个索引是一种浪费;如果不必要地添加较短的,请删除它。

【讨论】:

  • 很好的解释!谢谢!
  • @rick-james 1- 为什么在第一种情况下我们需要INDEX(table_2);如果约定到位并且table_2 确实是table2 的FK。 MySQL(InnoDB)不索引 FK 吗? 2- 你能详细说明后一种情况下的INDEX(table_2, table_3, table_1) 吗?我看到INDEX(value, id, foo2) 的必要性,但是...
  • @HasanCanSaral - 看看我添加的关于 FK 的内容是否解决了您的问题。
  • 谢谢。我现在明白了(至少是第一部分)。我特别要求this case。对 JOINS 的复合索引的需求。我非常感谢您的工作和帮助。谢谢。
  • @HasanCanSaral - 感谢您提出新问题;它与这个完全不同,值得单独讨论。
【解决方案2】:

如果这三个字段总是在一起,那么复合是有意义的。

但在您的情况下,您还需要一个用于 t2.value 的单

【讨论】:

    【解决方案3】:

    对于这个查询:

    SELECT * FROM tt 
        JOIN table1 t1 ON tt.table_1 = t1.id
        JOIN table2 t2 ON tt.table_2 = t2.id
        JOIN table3 t3 ON tt.table_3 = t3.id
    WHERE t2.value = 'test'
    

    是的,tt(table_1, table_2, table_3) 上的索引可能会有所帮助。

    您还需要以下列为索引:

    t1(id)
    t2(id, value)
    t3(id)
    

    正如 spencer7593 所评论的,您可能还想尝试t2(value, id) 而不是t2(id, value)。如果id 看起来像主键,那么前者应该更有效,因为它可以用于预过滤where 子句中的记录并有效地限制要加入的行数。

    索引是否有用取决于许多因素,例如表的相对大小和列内值的分布(或查询规划器根据此标准评估的内容)。您确实想使用EXPLAIN 并分析结果。

    【讨论】:

    • 感谢您的帮助,EXPLAIN 对我没有帮助,因为它只显示可能的键,但如果我重新创建复合索引并重新排列 table_1、table_2、table_3 - 我也认为这是可能的键。这是 EXPLAIN 的问题。 EXPLAIN 没有充分解释发生了什么。
    • 对于这个查询,我很想在 t2 上使用一个以 value 作为前导列的索引(因为相等条件)并包含 id 以使其成为覆盖索引“... on table2 (value,id)”。然后是 tt 上的索引,以 table_2 作为前导列“... on tt (table_2, ...)
    • @spencer7593:这是一个很好的观点,谢谢。我将其添加到我的答案中。
    • 另外,tt 索引不是最佳的。我在我的回答中详细说明。
    • @GMB - 在许多情况下,INDEX 中的列顺序很重要。 (在这种情况下为 t2 和 tt。)
    猜你喜欢
    • 2019-08-14
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 1970-01-01
    • 2020-06-14
    相关资源
    最近更新 更多