优化 MySQL 3 个表之间的左连接查询以减少执行时间
Optimizing MySQL Left join query between 3 tables to reduce execution time
我有以下查询:
SELECT region.id, region.world_id, min_x, min_y, min_z, max_x, max_y, max_z, version, mint_version
FROM minecraft_worldguard.region
LEFT JOIN minecraft_worldguard.region_cuboid
ON region.id = region_cuboid.region_id
AND region.world_id = region_cuboid.world_id
LEFT JOIN minecraft_srvr.lot_version
ON id=lot
WHERE region.world_id = 10
AND region_cuboid.world_id=10;
Mysql 慢速查询日志告诉我执行时间超过 5 秒,return 2300 行但检查了 15'404'545 行 return 它。
这三个表各有大约 6500 行,只有 id 和 lot 字段上的唯一键以及 world_id 字段上的键。我试图通过按 ID 和 world_id 上的双 WHERE 过滤长方体和世界来最大程度地减少检查的行数,但它似乎没有帮助。
知道如何优化这个查询吗?
Here is the sqlfiddle 包含当前状态的索引。
您已在 region.world_id 和 region_cuboid.world_id 上加入了两个表 "minecraft_worldguard.region" 和 "minecraft_worldguard.region_cuboid"。所以 WHERE 子句不需要两个条件。
WHERE 子句中的两列已在 JOIN 条件中等同,因此您不需要检查 WHERE 子句中的两个条件。在 WHERE 子句中删除其中一个,并在保留在 WHERE 条件的列上添加索引。
在您的示例中,将 WHERE 子句保留如下:
其中 region.world_id = 10
并在 region.world_id 列上添加索引,这会稍微提高性能。
注意:请注意,我建议您放弃 WHERE 子句的 "AND region_cuboid.world_id=10;" 部分。
希望对您有所帮助。
MySQL 在这种情况下不能使用索引,因为连接的字段具有不同的数据类型:
`lot` varchar(20) COLLATE utf8_unicode_ci NOT NULL
`id` varchar(128) COLLATE utf8_bin NOT NULL
如果您将此字段的类型更改为一般类型(例如,region.id
到 utf8_unicode_ci
),MySQL 使用主键(fiddle)。
根据docs:
Comparison of dissimilar columns (comparing a string column to a
temporal or numeric column, for example) may prevent use of indexes if
values cannot be compared directly without conversion.
首先,在编写具有多个 table 的查询时,习惯 "alias" 对 table 的引用是一件非常好的事情,这样您就不会重新键入整个长名称。此外,确定这些列来自哪些 table 是一个非常好的主意,这样可以让用户更好地了解什么是什么,这也有助于提高性能(例如建议覆盖索引)。
就是说,我已经为您的原始查询应用了别名,但我猜测每个列的 table,但您显然可以快速识别并进行调整。
SELECT
R.id,
R.world_id,
RC.min_x,
RC.min_y,
RC.min_z,
RC.max_x,
RC.max_y,
RC.max_z,
LV.version,
LV.mint_version
FROM
minecraft_worldguard.region R
LEFT JOIN minecraft_worldguard.region_cuboid RC
ON R.id = RC.region_id
AND R.world_id = RC.world_id
LEFT JOIN minecraft_srvr.lot_version LV
ON R.id = LV.lot
WHERE
R.world_id = 10
我还从 where 子句中删除了你的 "region_cuboid.world_id = 10",因为基于区域和世界的 JOIN 子句是多余的。
对于索引的建议,如果我有对列的正确别名引用,我会建议在区域 table 上使用覆盖索引
( world_id, id )。第一个位置的 "World_id" 快速限定 WHERE 子句,"id" 用于 RC 和 LV tables。
对于 region_cuboid table,我还会在 (world_id, region_id) 上有一个索引,以匹配要加入的区域 table它。
对于lot_versiontable,索引在(lot)上或覆盖索引在(lot,version,mint_version)
我有以下查询:
SELECT region.id, region.world_id, min_x, min_y, min_z, max_x, max_y, max_z, version, mint_version
FROM minecraft_worldguard.region
LEFT JOIN minecraft_worldguard.region_cuboid
ON region.id = region_cuboid.region_id
AND region.world_id = region_cuboid.world_id
LEFT JOIN minecraft_srvr.lot_version
ON id=lot
WHERE region.world_id = 10
AND region_cuboid.world_id=10;
Mysql 慢速查询日志告诉我执行时间超过 5 秒,return 2300 行但检查了 15'404'545 行 return 它。
这三个表各有大约 6500 行,只有 id 和 lot 字段上的唯一键以及 world_id 字段上的键。我试图通过按 ID 和 world_id 上的双 WHERE 过滤长方体和世界来最大程度地减少检查的行数,但它似乎没有帮助。
知道如何优化这个查询吗?
Here is the sqlfiddle 包含当前状态的索引。
您已在 region.world_id 和 region_cuboid.world_id 上加入了两个表 "minecraft_worldguard.region" 和 "minecraft_worldguard.region_cuboid"。所以 WHERE 子句不需要两个条件。
WHERE 子句中的两列已在 JOIN 条件中等同,因此您不需要检查 WHERE 子句中的两个条件。在 WHERE 子句中删除其中一个,并在保留在 WHERE 条件的列上添加索引。
在您的示例中,将 WHERE 子句保留如下: 其中 region.world_id = 10
并在 region.world_id 列上添加索引,这会稍微提高性能。
注意:请注意,我建议您放弃 WHERE 子句的 "AND region_cuboid.world_id=10;" 部分。
希望对您有所帮助。
MySQL 在这种情况下不能使用索引,因为连接的字段具有不同的数据类型:
`lot` varchar(20) COLLATE utf8_unicode_ci NOT NULL
`id` varchar(128) COLLATE utf8_bin NOT NULL
如果您将此字段的类型更改为一般类型(例如,region.id
到 utf8_unicode_ci
),MySQL 使用主键(fiddle)。
根据docs:
Comparison of dissimilar columns (comparing a string column to a temporal or numeric column, for example) may prevent use of indexes if values cannot be compared directly without conversion.
首先,在编写具有多个 table 的查询时,习惯 "alias" 对 table 的引用是一件非常好的事情,这样您就不会重新键入整个长名称。此外,确定这些列来自哪些 table 是一个非常好的主意,这样可以让用户更好地了解什么是什么,这也有助于提高性能(例如建议覆盖索引)。
就是说,我已经为您的原始查询应用了别名,但我猜测每个列的 table,但您显然可以快速识别并进行调整。
SELECT
R.id,
R.world_id,
RC.min_x,
RC.min_y,
RC.min_z,
RC.max_x,
RC.max_y,
RC.max_z,
LV.version,
LV.mint_version
FROM
minecraft_worldguard.region R
LEFT JOIN minecraft_worldguard.region_cuboid RC
ON R.id = RC.region_id
AND R.world_id = RC.world_id
LEFT JOIN minecraft_srvr.lot_version LV
ON R.id = LV.lot
WHERE
R.world_id = 10
我还从 where 子句中删除了你的 "region_cuboid.world_id = 10",因为基于区域和世界的 JOIN 子句是多余的。
对于索引的建议,如果我有对列的正确别名引用,我会建议在区域 table 上使用覆盖索引 ( world_id, id )。第一个位置的 "World_id" 快速限定 WHERE 子句,"id" 用于 RC 和 LV tables。
对于 region_cuboid table,我还会在 (world_id, region_id) 上有一个索引,以匹配要加入的区域 table它。
对于lot_versiontable,索引在(lot)上或覆盖索引在(lot,version,mint_version)