加快返回许多结果的mysql查询

Posted

技术标签:

【中文标题】加快返回许多结果的mysql查询【英文标题】:Speed up mysql query that returns many results 【发布时间】:2015-07-09 12:45:21 【问题描述】:

我正在尝试加速返回大量结果的 mysql 查询。 这个想法是显示一些按类别/周等划分的结果。但是加载它们需要永远。完全无法使用。

您能提出解决方案吗?当我不得不处理这种情况时,我有哪些选择?

SELECT  DISTINCT vowao.id_product_attribute, WEEKOFYEAR(vowao.date_purchased) AS week,
        YEAR(vowao.date_purchased) AS year, 
      ( SELECT  COUNT(*)
            FROM  ps_view_orders_w_attributes_ordered vowao_s
            WHERE  WEEKOFYEAR(vowao_s.date_purchased) = WEEKOFYEAR(vowao.date_purchased)
              AND  vowao_s.id_product_attribute = vowao.id_product_attribute
      ) AS amount_sold,
      ( SELECT  al.public_name
            FROM  ps_attribute_lang al
            WHERE  al.id_attribute = vpac.id_attribute
              AND  al.id_lang='2'
      ) AS name, 
      ( SELECT  al.id_attribute
            FROM  ps_attribute_lang al
            WHERE  al.id_attribute = vpac.id_attribute
              AND  al.id_lang='2'
      ) AS id_attribute
    FROM  `ps_view_orders_w_attributes_ordered` vowao
    JOIN  `ps_view_product_attribute_combination` vpac
            ON vpac.id_product_attribute = vowao.id_product_attribute
    WHERE  vowao.id_shop = '".$id_shop."'
      AND  vpac.is_color_group = 1
      AND  WEEKOFYEAR(vowao.date_purchased) IS NOT NULL
      AND  YEAR(vowao.date_purchased) = 2015

【问题讨论】:

相关子查询会很慢。你能改变吗? 我试图移动那些子查询(计数等)并将它们放在一个 forloop 中。因此,获取结果并在 for 循环中获取计数,在另一个 for 循环中获取名称等。但不幸的是,我没有看到结果检索速度的任何变化 没有。不是循环。只是一个不相关的子查询 - 实际上只是后两个的连接,因为其中没有聚合。另外,WEEKOFYEAR(date_purchased) IS NOT NULL 肯定与date_puchased IS NOT NULL 相同?? 是的 date_purchased 是一样的。因此,如果我使用不相关的子查询,您认为它会更快吗?因为现在它几乎无法使用:) 如果ps_view_orders_w_attributes_ordered 是一个常规的索引表,那么是的。 【参考方案1】:

暂时忽略聚合部分,这样的事情会更快...

 SELECT DISTINCT v.id_product_attribute
               , DATE_FORMAT(v.date_purchased,'%v-%x') yearweek
               , al.public_name
               , al.id_attribute
            FROM ps_view_orders_w_attributes_ordered v
            JOIN ps_view_product_attribute_combination vpac 
              ON vpac.id_product_attribute = v.id_product_attribute
            JOIN ps_attribute_lang al 
              ON al.id_lang = vpac.id_attribute
           WHERE v.id_shop = '$id_shop'
             AND vpac.is_color_group = 1
             AND v.date_purchased BETWEEN '2015-01-01' AND '2015-12-31'
             AND v.date_purchased IS NOT NULL
             AND al.id_attribute = 2;

下一步是查看 EXPLAIN,并酌情添加索引。

【讨论】:

【参考方案2】:

我试了一下。以下是我的目标:

    没有相关的子查询 没有DISTINCT(可能是最让你慢下来的原因) 约束中没有不必要的公式 所有标准 SQL(因为我对 MySQL 语法不太熟悉!)

这是查询:

SELECT 
    vowao.id_product_attribute, 
    WEEKOFYEAR(vowao.date_purchased) AS week, 
    YEAR(vowao.date_purchased) AS year,
    amount_group.amount_sold,
    al.public_name AS name,
    al.id_attribute AS id_attribute
FROM 
    `ps_view_orders_w_attributes_ordered` vowao
JOIN 
    `ps_view_product_attribute_combination` vpac 
    ON 
    vpac.id_product_attribute = vowao.id_product_attribute
LEFT JOIN
     ps_attribute_lang al 
     ON 
     al.id_attribute = vpac.id_attribute 
     AND 
     al.id_lang='2'
LEFT JOIN
    (SELECT 
        WEEKOFYEAR(date_purchased) as week_of_year,
        id_product_attribute,
        COUNT(*) as amount_sold
    FROM
        ps_view_orders_w_attributes_ordered
    GROUP BY
        WEEKOFYEAR(date_purchased), 
        id_product_attribute
    ) amount_group
    ON
    WEEKOFYEAR(vowao.date_purchased)  = amount_group.week_of_year
    AND
    vowao.id_product_attribute = amount_group.id_product_attribute
WHERE 
    vowao.id_shop = '".$id_shop."'
    AND 
    vpac.is_color_group = 1
    AND 
    vowao.date_purchased IS NOT NULL
    AND 
    YEAR(vowao.date_purchased) = 2015
GROUP BY
    vowao.id_product_attribute,
    WEEKOFYEAR(vowao.date_purchased),
    YEAR(vowao.date_purchased),
    al.public_name,
    al.id_attribute

【讨论】:

它仍然很慢 :( 但感谢您的建议,将牢记这一点并尝试改进一般的 sql 查询 如果所有这些替代方案都很慢,那么您可能会丢失表所需的所有重要索引。也许如果您可以为这些表提供完整的架构,我们可以提供更多帮助。否则,您将独自使用EXPLAIN【参考方案3】:

您可以尝试的一件事是将相关子查询转换为连接:

SELECT DISTINCT
    vowao.id_product_attribute,
    WEEKOFYEAR(vowao.date_purchased) AS week,
    YEAR(vowao.date_purchased) AS year,
    amounts_sold.total,
    attr_names.id_attribute,
    attr_names.public_name AS name,
FROM `ps_view_orders_w_attributes_ordered` vowao
JOIN `ps_view_product_attribute_combination` vpac ON vpac.id_product_attribute = vowao.id_product_attribute
LEFT JOIN (
    SELECT WEEKOFYEAR(vowao_s.date_purchased) AS s_week, vowao_s.id_product_attribute AS s_attr, COUNT(*) AS total
    FROM ps_view_orders_w_attributes_ordered vowao_s
    GROUP BY WEEKOFYEAR(vowao_s.date_purchased), vowao_s.id_product_attribute
) amounts_sold ON amounts_sold.s_week = WEEKOFYEAR(vowao.date_purchased) AND amounts_sold.s_attr = vowao.id_product_attribute
LEFT JOIN (
    SELECT al.id_attribute, al.public_name
    FROM ps_attribute_lang al
    WHERE al.id_lang='2'
) attr_names ON al.id_attribute = vpac.id_attribute
WHERE vowao.id_shop = '".$id_shop."'
  AND vpac.is_color_group = 1
  AND WEEKOFYEAR(vowao.date_purchased) IS NOT NULL
  AND YEAR(vowao.date_purchased) = 2015

要获得更好/更多帮助,请在查询中显示使用 EXPLAIN 的结果。您还可以使用相关架构和示例数据创建 SQL Fiddle 以帮助帮助者。

【讨论】:

如果这样更快,我打印出来吃掉。 我觉得速度差不多。但是谢谢 :)【参考方案4】:

al 需要INDEX(id_lang, id_attribute)(或相反的顺序)

众所周知,键值 (EAV) 架构笨拙且效率低下。

AND YEAR(vowao.date_purchased) = 2015 在函数中隐藏了一个潜在的索引列,从而消除了使用索引的可能性。相反,做

AND vowao.date_purchased >= '2015-01-01
AND vowao.date_purchased <  '2015-01-01 + INTERVAL 1 YEAR

摆脱这个,因为上面的查询会因为NULL而失败:

AND  WEEKOFYEAR(vowao.date_purchased) IS NOT NULL

完成这些后,添加

INDEX(id_shop, date_purchased)

请为每张桌子提供SHOW CREATE TABLE,这样我们就不必猜测出了什么问题。

【讨论】:

以上是关于加快返回许多结果的mysql查询的主要内容,如果未能解决你的问题,请参考以下文章

添加 Limit 可以加快 mysql 查询速度,为啥?

MySQL 查询优化以获得月度报告

假如mysql数据库单表有100W行记录,有哪些方式加快查询速度

这两个mysql查询之间的区别?

如何在 MySQL 查询中返回相关字段而不是参考号

mysql 返回的查询结果为空