优化包含重复查询作为子查询的 MySQL 查询?
Posted
技术标签:
【中文标题】优化包含重复查询作为子查询的 MySQL 查询?【英文标题】:Optimizing MySQL query that includes a repeated query as a subquery? 【发布时间】:2016-07-18 12:05:57 【问题描述】:假设我有这个查询:
(SELECT pets.id, "pets" AS "table" FROM pets WHERE name="Joey")
UNION ALL
(SELECT toys.id, "toys" AS "table" FROM (SELECT id, "pets" AS "table" FROM pets WHERE name="Joey") AS parentQuery, toys WHERE type="Ball" AND pet_id=parentQuery.id)
UNION ALL
(SELECT owners.id, "owners" AS "table" FROM (SELECT id, "pets" AS "table" FROM pets WHERE name="Joey") AS parentQuery, owners WHERE name="Issac" AND pet_id=parentQuery.id)
SQL 小提琴:http://sqlfiddle.com/#!9/1637a/1
这里的想法是在多个一对多场景中获取相关行的 ID 列。 几个问题:
-
虽然我希望 mysql 在某种程度上通过查询缓存结果进行优化,以便将相同的初始查询作为子查询嵌入到其他
UNION
'ed 查询中,但是否可以安全、干净地使用SQL 变量(或其他)在其他查询中引用第一个查询的结果...以避免重复查询和任何捆绑参数再次为每个附加UNION
'ed 查询?
(可选) 如果您认为这是糟糕/鲁莽的 SQL 设计,请告诉我。我确信这可以通过JOIN
变体来完成,但我只是不确定有很多连接的性能是否会比像这样拆分它更好?我听到了不同的意见。
谢谢!我已经看到了这个响应,并认为类似的东西可以工作,但到目前为止我通常避免使用 SQL 变量,所以我不确定如何使用UNION
'ed 查询来构造它:How to optimize huge query with repeated subqueries
【问题讨论】:
我觉得你可以尽可能快地测试排列。 好吧,我的第一个问题在这里最重要...避免在后续查询中重新捆绑初始查询。到目前为止,性能在测试中很好。 如果性能好删除问题。这是过早的优化。 AFAIK。由于 mysql 没有公用表表达式,因此无法避免重写该查询。 我为什么要删除问题?如果想回答它无法完成(请求......避免重复查询/参数),那就这么说。其他人可能有同样的问题! 你能分享一个sql fiddle吗? 【参考方案1】:我想再补充一些东西。
1) 使用inner join
转换您的查询。
我个人认为 INNER JOIN 更好,因为它更 可读。 更好地显示了表之间的关系。你得到了 连接中的那些关系,然后您在 WHERE 中进行过滤 条款。这种分离使查询更具可读性。但这是一个 个人喜好问题。
因此,基于此选择,这是您转换后的查询:
SELECT
pets.id,
"pets" AS "table"
FROM pets
WHERE name= "Joey"
UNION ALL
SELECT
owners.id,
"pets" AS "table"
FROM pets
INNER JOIN owners ON pets.id = owners.pet_id
WHERE pets.`name` = "Joey"
AND owners.`name`= "Issac"
UNION ALL
SELECT
toys.id,
"toys" AS "table"
FROM toys
INNER JOIN pets ON toys.pet_id = pets.id
WHERE pets.Name="Joey"
AND toys.type="Ball";
2) 尽管您当前的查询表现良好,但我认为您可以通过在表中创建一些 composite index
来进一步提高性能。
pets
表需要name,id
上的复合索引。
toys
表需要pet_id,type
上的复合索引。
owners
表需要pet_id,name
上的复合索引。
编辑:
这是创建了上述索引的这三个表的表结构。
-- ----------------------------
-- Table structure for `owners`
-- ----------------------------
DROP TABLE IF EXISTS `owners`;
CREATE TABLE `owners` (
`id` bigint(20) DEFAULT NULL,
`pet_id` bigint(20) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
KEY `Idx_owners_pet_id_name` (`pet_id`,`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `pets`
-- ----------------------------
DROP TABLE IF EXISTS `pets`;
CREATE TABLE `pets` (
`id` bigint(20) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
KEY `Idx_pets_name_pet_id` (`name`,`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `toys`
-- ----------------------------
DROP TABLE IF EXISTS `toys`;
CREATE TABLE `toys` (
`id` bigint(20) DEFAULT NULL,
`pet_id` bigint(20) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
KEY `Idx_toys_pet_id_type` (`pet_id`,`type`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
注意:稍后您可以通过EXPLAIN (MySQL)
查看查询性能。确保这三个表中有大量数据。 因为您无法在小数据集上判断性能。
【讨论】:
因此,在投入生产之前,索引当然是其中的一部分。除了JOIN
,有没有办法避免重复查询?
我认为在这种情况下是不可能的以上是关于优化包含重复查询作为子查询的 MySQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章