【问题标题】:Optimize multi-join query, please请优化多连接查询
【发布时间】:2012-08-23 18:45:54
【问题描述】:

我查询了 sql,但它真的很重,我真的不知道如何优化它,解释在后面:

SELECT tyds.user_tyd, 
       tyds.product_tyd, 
       tyds.action_tyd, 
       products.price_product, 
       data_8, 
       data_9, 
       avg_8, 
       sum_9 
FROM   tyds 
       INNER JOIN products 
               ON tyds.product_tyd = products.id_product 
       INNER JOIN (SELECT product_tyd, 
                          Avg(data_tyd) AS avg_8 
                   FROM   tyds 
                   WHERE  action_tyd = 8 
                   GROUP  BY product_tyd) Agg_1 
               ON Agg_1.product_tyd = tyds.product_tyd 
       INNER JOIN (SELECT product_tyd, 
                          Sum(data_tyd) AS sum_9 
                   FROM   tyds 
                   WHERE  action_tyd = 9 
                   GROUP  BY product_tyd) Agg_2 
               ON Agg_2.product_tyd = tyds.product_tyd 
       INNER JOIN (SELECT product_tyd, 
                          data_tyd AS data_8 
                   FROM   tyds 
                   WHERE  user_tyd = 3 
                          AND action_tyd = 8) Agg_3 
               ON Agg_3.product_tyd = tyds.product_tyd 
       INNER JOIN (SELECT product_tyd, 
                          data_tyd AS data_9 
                   FROM   tyds 
                   WHERE  user_tyd = 3 
                          AND action_tyd = 9) Agg_4 
               ON Agg_4.product_tyd = tyds.product_tyd 
WHERE  tyds.user_tyd = 3 
       AND tyds.action_tyd = 1 
GROUP  BY tyds.product_tyd 

所有这些都是因为我需要得到很多东西: 我在 tyds.user_tyd = 3 中定义了一个 id_user tyds.action_tyd = 1 因为我想按 tyds.product_tyd 分组 WHERE 以前的 id_user 作为 tyds.action_tyd = 1。

然后我想要: -data_8 是 tyds.data_tyd 的值,其中 user_tyd = 3 AND action_tyd = 8 -data_9 也一样

-对于 AVG 和 COUNT,我想跳过 tyds.user_tyd 条件,只按 id_product 分组。

实际上,这个查询是有效的,但我认为它真的很重并且有太多的 SELECT...

一个月前我提出了一个类似的问题,但我不得不审查我的架构,所以很抱歉......

非常感谢。

e1 : 我使用 MySQL

【问题讨论】:

  • 是否需要在一个查询中保存?
  • 嗯,它会执行很多时间,所以我认为它更好。而且我将有一些页面包含 3 种此类...实际上看起来并不太慢,但我更喜欢考虑 optimiz。现在。
  • 好的,我明白你的意思。我的建议是为内部连接中的每个子查询准备一个临时表。我相信它会更快。如果您不想这样做,请告诉我们您使用哪些 dbms。你有关于表的索引吗?
  • 这似乎很有趣,即使我不知道你是怎么做到的。我在每个 id_TABLE 上都有索引。
  • +1 给格式化该 sql 的人。

标签: mysql sql query-optimization average


【解决方案1】:

您实际上并不需要所有这些连接和子查询;您可以使用CASE expressions 将逻辑从WHERE-clauses 移动到字段列表中,从而消除其中的大部分:

SELECT 3 AS user_tyd, tyds.product_tyd, 1 AS action_tyd,
       products.price_product,
       MAX(CASE WHEN user_tyd = 3 AND action_tyd = 8 THEN data_tyd END) data_8,
       MAX(CASE WHEN user_tyd = 3 AND action_tyd = 9 THEN data_tyd END) data_9,
       AVG(CASE WHEN action_tyd = 8 THEN data_tyd END) AS avg_8,
       SUM(CASE WHEN action_tyd = 9 THEN data_tyd END) AS sum_9
  FROM tyds
  JOIN products
    ON tyds.product_tyd = products.id_product
 WHERE tyds.product_tyd IN
         ( SELECT product_tyd
             FROM tyds
            WHERE tyds.user_tyd = 3
              AND tyds.action_tyd = 1
         )
 GROUP
    BY tyds.product_tyd
;

(注意:第三行和第四行中的MAX(...) 只是强制非空值优先于空值的一种方式。)

你必须测试一下这是否真的更快,但我敢打赌。

【讨论】:

  • 是的,它快了十倍……非常感谢。定义 3 AS user_tyd 有什么意义?
  • @pierreaurelemartin:不客气!而且,3 AS user_tyd 的意义在于得到user_tyd 的正确值:我不得不从WHERE 子句中删除tyds.user_tyd = 3,因为avg_8sum_9 的计算必须考虑到其他记录,因此在字段列表中使用 tyds.user_tyd 会给出不确定(即错误)的结果。
  • 好的,我不太了解一切是如何工作的,但我会分解您的查询以获取它。再次感谢。
【解决方案2】:

简单示例,仅适用于第一个 Agg_%

create temporary table Agg_1
(
  product_tyd ...,
  avg_8 ...
)

insert into Agg_1
SELECT product_tyd, AVG(data_tyd) AS avg_8 FROM tyds WHERE action_tyd = 8 
GROUP BY product_ty

SELECT  tyds.user_tyd, tyds.product_tyd, tyds.action_tyd, products.price_product,
data_8, data_9, avg_8, sum_9 
FROM tyds 
INNER JOIN products 
ON tyds.product_tyd = products.id_product 
INNER JOIN Agg_1 ON Agg_1.product_tyd = tyds.product_tyd
...

现在,我看到您正在使用 Mysql。我不确定它是否适用于 MySql,我不是 MySql 大师。但是,我确信它适用于 oracle 和 sybase。

【讨论】:

  • 它适用于 mysql。我将测试该解决方案以与其他解决方案进行比较。
猜你喜欢
  • 2020-04-30
  • 2020-01-02
  • 2017-04-24
  • 2021-12-11
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 2021-12-20
  • 1970-01-01
相关资源
最近更新 更多