优化包含重复查询作为子查询的 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 查询?的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL 中针对子查询的查询优化? [关闭]

MySql查询优化方法总结

MySql学习 —— 数据库优化理论 —— 查询优化技术

MySQL 删除子查询中的重复子句

查询优化。重复子查询

MySQL 索引优化与子查询与左连接