mysql - 具有多个 JOIN varchar 条件的表的索引

标签 mysql innodb

我正在尝试进行某种匹配查询,以使用多列匹配从表 A 到表 B 的记录。但由于某种原因,查询运行速度很慢。我尝试为连接表尝试不同的索引组合,但它们没有被使用,这就是它总是进行全表扫描的原因。

SELECT
    nc.id, nc.firstName, nc.lastName, 
    nc.firmName, nc.location,
    nc.city AS city, nc.state AS state, 
    ac.id, ac.lastName, ac.firstName, ac.middleName, 
    IFNULL(ac.suffixName, '') AS suffixName, ac.firmName, ac.city, ac.state
FROM
    NormalContacts AS nc
JOIN
    AllContacts AS ac ON ((nc.firstName = ac.firstName AND nc.lastName = fa.lastName) OR (nc.firstName = fa.middleName AND nc.lastName = ac.lastName))
        AND (ac.city = nc.city AND ac.state = nc.state)
JOIN
    FirmInputTable AS fit ON (fit.firmName = fa.firmName AND fit.otherFirmName = nc.firmName)       
WHERE
    nc.crdNumber IS NULL AND nc.city IS NOT NULL AND nc.state IS NOT NULL AND nc.firmName IS NOT NULL

NormalContacts 是 1,000 条记录,而 AllContacts 是 337,250 条记录。 JOIN 条件中的所有字段都是 varchar。

这是 EXPLAIN 结果:

enter image description here

*fa 在屏幕截图 AllContacts 中,对于打字错误感到抱歉。

希望任何人都可以建议一种优化此查询的方法或告诉我我做错了什么。提前致谢!

最佳答案

优化器将查看索引和统计信息来决定对表执行的顺序。

在下文中,我将假设 ON 仅说明表之间的关系,而 WHERE 仅用于过滤。

“第一个”表可能(但不一定)是具有最有选择性的 WHERE 条件的表。因此,INDEX 需要关注WHERE 中的列。 (有时 GROUP BYORDER BY 发挥作用。)

JOIN 序列中的其他表将通过“嵌套循环连接”进入。这是“对于前一个表中的每一行,获取当前表中的行”的地方。要执行此提取操作,它将使用与该表相关的任何 WHERE 子句以及 ON 中提到此表(可能还有之前的表)的任何内容。因此,从 WHEREON 条件考虑此表的 INDEX

由于您无法始终预测优化器遍历表的顺序,因此最好添加索引以适应每个可能的顺序。

一个典型的快捷方式是当 WHERE 只提到一个表时。那张 table 几乎肯定会先被选中。

如何为一个特定的表建立一个好的索引,给定 WHERE/ON 导致它的东西?参见 my cookbook

对于你的情况......

您似乎正确地拆分了 ONWHERE -- 很好。

nc 似乎是 WHERE 中提到的唯一表,因此我们可以假设优化器将从它开始。

IS NULL 类似于= constant,但是IS NOT NULL 就像一个范围,不是那么容易优化。我推荐

nc:  INDEX(crdNumber, state)

(在这里,我猜测哪个 nc 列最有可能/最不可能为 NULL。)

nc之后,只有ac(又名fa???)可以来:

ON    ((...) OR (...))
  AND ac.city = nc...
  AND ac.state = nc...

OR 通常无法索引或优化,所以我们只剩下

ac:  INDEX(city, state)  -- in either order

可能 lastName 可以从 OR 中取出(在固定 ac/fa 之后),从而导致

ac:  INDEX(city, state, firstName)  -- in any order

最后,适合:

ON    fit.firmName = ...
  AND fit.otherFirmName = ...

导致

fit:  INDEX(firmName, otherFirmName)  -- in either order

修复查询中的fa;如果需要,我会修改我的答案。

注意:在这些情况下,INDEX(a,b) 优于 INDEX(a), INDEX(b)

关于mysql - 具有多个 JOIN varchar 条件的表的索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47428452/

相关文章:

sql - 如何避免子查询?

PHP如何转义pgp公钥以插入数据库

php - 我可以为 Linux 上的 MySQL 设置不同的连接与查询超时值吗?

mysql - 如何在 SQL 中按日期范围连接多个价格表?

分类字段和时间戳列(varchar)上的mysql分区

mysql - 数据透视表的外键和约束,删除级联

php - 创建 CRON 作业来获取/发送数据库值

MySQL 自动增量=535

MySQL 使用 DUPLICATE KEY UPDATE 时出错

mysql - SQLSTATE[40001] : Serialization failure: 1213 Deadlock issue caused by INSERT trigger on concurrent access