需要帮助优化带有 Journal 主题的 Opencart 中的 SQL 查询

问题描述 投票:0回答:1

我正在开发一个使用 Opencart 和 Journal 主题的项目,并且遇到了复杂 SQL 查询的性能问题。此查询负责获取产品数据,包括评级、折扣、特价和销售数量。不过好像有点慢,正在寻找优化的方法。我有由它引起的长达 55 秒的负载。它消耗并达到我的 CPU 限制。

这是我当前使用的 SQL 查询:

EXPLAIN
SELECT
    p.product_id,
    (
        SELECT AVG(rating) total
        FROM `oc_review` r1
        WHERE r1.product_id = p.product_id
        AND r1.status = '1'
        GROUP BY r1.product_id
    ) rating,
    (
        SELECT price
        FROM `oc_product_discount` pd2
        WHERE pd2.product_id = p.product_id
        AND pd2.customer_group_id = '1'
        AND pd2.quantity = '1'
        AND (
            (pd2.date_start = '0000-00-00' OR pd2.date_start < NOW())
            AND (pd2.date_end = '0000-00-00' OR pd2.date_end > NOW())
        )
        ORDER BY pd2.priority ASC, pd2.price ASC
        LIMIT 1
    ) discount,
    (
        SELECT price
        FROM `oc_product_special` ps
        WHERE ps.product_id = p.product_id
        AND ps.customer_group_id = '1'
        AND (
            (ps.date_start = '0000-00-00' OR ps.date_start < NOW())
            AND (ps.date_end = '0000-00-00' OR ps.date_end > NOW())
        )
        ORDER BY ps.priority ASC, ps.price ASC
        LIMIT 1
    ) special,
    p.viewed,
    SUM(op.quantity) AS sales
FROM `oc_order_product` op
LEFT JOIN `oc_order` o ON (o.order_id = op.order_id)
LEFT JOIN `oc_product` p ON (p.product_id = op.product_id)
LEFT JOIN `oc_product_description` pd ON (p.product_id = pd.product_id)
LEFT JOIN `oc_product_to_store` p2s ON (p.product_id = p2s.product_id)
WHERE p.status = '1'
AND p.date_available <= NOW()
AND p2s.store_id = '0'
AND pd.language_id = '2'
AND o.order_status_id > '0'
GROUP BY p.product_id
ORDER BY sales DESC, LCASE(pd.name) DESC
LIMIT 0, 100;

我怀疑可以优化该查询以提高性能。任何关于如何提高效率的建议或见解将不胜感激。 谢谢大家

这是我迄今为止尝试过的:

我已检查 SQL 查询的正确性并确保其按预期运行。 我已经验证相关表的必要索引是否已就位。 我已检查服务器环境是否存在任何潜在的资源瓶颈。

但是,我面临着一个性能问题,该查询似乎执行缓慢。我相信可能还有优化的空间,但由于我有限的数据库专业知识,我正在努力找出需要改进的确切领域。

我的期待:

我正在寻求 Stack Overflow 社区的指导和专业知识来帮助我理解:

所提供的查询是否存在任何明显的性能瓶颈或效率低下。 潜在的优化或替代方法可以更有效地实现相同的结果。 有关如何解释查询执行计划或对其进行分析以获得更好性能的任何见解或建议。

sql database indexing query-optimization opencart
1个回答
0
投票

该查询中有很多小问题和一些大问题。我将在此列出对我来说最突出的内容 - 其中一些可能涉及对应用程序核心的重大更改,因为它存在一些设计问题(自 2012 年以来我一直在使用 Opencart)。

  1. 由于
    pd2.date_start = '0000-00-00' OR pd2.date_start < NOW()
    的存在,所有具有类似
    OR
    条件的子查询都无法使用索引(如果您的版本中有索引)。在管理面板中,当您将这些字段留空时,它们会被写为
    0000-00-00
    ,这需要同时检查这两个字段 - 更不用说这是非法的日期时间值,因此最好不要使用它。更好的方法是在留空时将
    date_start
    设置为
    0000-01-01
    ,将
    date_end
    设置为
    9999-01-01
    ,然后将查询从:
AND (
    (pd2.date_start = '0000-00-00' OR pd2.date_start < NOW())
    AND (pd2.date_end = '0000-00-00' OR pd2.date_end > NOW())
)

AND pd2.date_start < NOW())
AND pd2.date_end > NOW())

Voilà,现在您可以在搜索字段上添加完整索引。即使您不这样做,DBMS 也能够显着优化。这里的挑战显然是您需要重写一些管理逻辑。我过去曾在一个网站上这样做过,性能提升非常可观。

  1. 所有这些
    LEFT JOINS
    都是废话。底部的
    WHERE
    条件需要它们,因此它们应该是
    RIGHT
    INNER
    连接(MySQL 中也是如此)。你可以重写这个:
LEFT JOIN `oc_order` o ON (o.order_id = op.order_id)
...
WHERE
...
AND o.order_status_id > '0'

像这样(没有说明符的

JOIN
默认为
INNER JOIN
,因此您可以省略关键字以便于阅读):

JOIN `oc_order` o ON o.order_id = op.order_id AND o.order_status_id > 0

其他人也一样。我希望这是有道理的 - 重点是,如果需要表格,将您的条件放入

JOIN

  1. 根据数据集的大小,按销售额总和等汇总值进行分组和排序并期望事情进展顺利将会很棘手。我怀疑这个查询用于“畅销书”视图或类似的东西。在这种情况下,最好创建一个特殊的搜索表,或者向产品表中添加一个列,其中的值每天都会更新。您不需要总销售额的前沿数字 - 只需要让您的畅销书名列前茅即可。当然 - 您需要使用 cron 定期更新该列或在下订单时更新该列。

  2. pd.name
    可能不区分大小写。失去
    LCASE()
    函数以获得一些性能并可能使用索引。

无论如何,这将是一个开始。干杯。

© www.soinside.com 2019 - 2024. All rights reserved.