需要帮助优化复杂的MySQL查询

时间:2010-09-04 22:39:13

标签: php sql mysql aggregate-functions

我在下面有这个查询。涉及4个主要表:tblOrder,tblItems,tblOrder_archive,tblItem_archive。几个月后,订单和项目被移动到表的存档版本,以免减慢主表查询的速度。 (销售和交通非常高)。因此,为了获得销售数据,我从每组表中选择我需要的东西(存档和非存档)。将它们联合起来...在联合上做一个组...然后对结果做一些数学运算。

问题在于任何大量的行(订单时间跨度)..查询运行需要很长时间才会超时。我已经添加了我能想到的所有按键并且仍然运行超慢。

我能做些什么来让这个跑得更快?我可以用不同的方式写吗?我可以使用不同的索引吗?

或者我应该编写一个首先从每个表集中获取数据的脚本然后在php脚本中进行数学运算来组合它们吗?

感谢您的帮助。

SELECT
  description_invoice
, supplier
, type
, sum(quantity) AS num_sold
, sum(quantity*wholesale) AS wholesale_price
, sum(quantity*price) AS retail_price
, sum(quantity*price) - sum(quantity*wholesale) AS profit
FROM (
    SELECT
      tblOrder.*
    , tblItem.description_invoice
    , tblItem.type
    , tblItem.product_number
    , tblItem.quantity
    , tblItem.wholesale
    , tblItem.price
    , tblItem.supplier 
    FROM tblOrder USE KEY (finalized), tblItem
    WHERE
      tblItem.order_id = tblOrder.order_id 
     AND
      finalized=1
     AND
      wholesale <> 0
     AND (order_time >= 1251788400 AND order_time <= 1283669999) 

  UNION 

    SELECT
      tblOrder_archive.*
    , tblItem_archive.description_invoice
    , tblItem_archive.type
    , tblItem_archive.product_number
    , tblItem_archive.quantity
    , tblItem_archive.wholesale
    , tblItem_archive.price
    , tblItem_archive.supplier
    FROM tblOrder_archive USE KEY (finalized), tblItem_archive
    WHERE
      tblItem_archive.order_id=tblOrder_archive.order_id
     AND
      finalized=1
     AND
      wholesale <> 0
     AND (order_time >= 1251788400 AND order_time <= 1283669999)
) AS main_table
GROUP BY
  description_invoice
, supplier,type 
ORDER BY profit DESC;

3 个答案:

答案 0 :(得分:2)

  • 在WHERE子句中使用的列上创建索引。
  • 删除索引提示:USE KEY (finalized)。如果它做任何事情,它可能会让MySQL选择这个密钥而不是一个可能更好的密钥,从而使速度变慢。
  • 添加LIMIT以避免获取太多行。如果要查看更多行,请使用分页。
  • 使用UNION ALL而不是UNION。这样会更快,因为它不会检查重复项,也可能你不想在这里删除重复项,因为这会影响总数。
  

订单和商品会在几个月后转移到表格的存档版本,以免减慢主表格查询的速度。

这可能是一个坏主意。相反,您应该正确地索引数据,以便在添加更多数据时查询不会变得非常慢。或者您可以查看partitioning the table

答案 1 :(得分:1)

我将您的查询重写为:

   SELECT COALESCE(x.description_invoice, y.description_invoice) AS description_invoice,
          COALESCE(x.supplier, y.supplier) AS supplier,
          COALESCE(x.type, y.type) AS type,
          COALESCE(SUM(x.quantity), 0) + COALESCE(SUM(y.quantity), 0) as num_sold,
          COALESCE(SUM(x.quantity * x.wholesale), 0) + COALESCE(SUM(y.quantity * y.wholesale), 0) AS wholesale_price, 
          COALESCE(SUM(x.quantity * x.price), 0) + COALESCE(SUM(y.quantity * y.price), 0) AS retail_price,
          COALESCE(SUM(x.quantity * x.price), 0) - COALESCE(SUM(x.quantity * x.wholesale), 0) + COALESCE(SUM(y.quantity * y.price), 0) - COALESCE(SUM(y.quantity * y.wholesale), 0) as profit   
     FROM (SELECT o.order_id
             FROM TBLORDER o
            WHERE o.finalized = 1
              AND o.order_time BETWEEN 1251788400 
                                   AND 1283669999
           UNION ALL
           SELECT oa.order_id
            FROM TBLORDER_ARCHIVE oa
           WHERE oa.finalized = 1
             AND oa.order_time BETWEEN 1251788400 
                                   AND 1283669999) a
LEFT JOIN TBLITEM x ON x.order_id = a.order_id
                   AND x.wholesale != 0
LEFT JOIN TBLITEM_ARCHIVE y ON y.order_id = a.order_id
                           AND y.wholesale != 0
 GROUP BY description_invoice, supplier, type 
 ORDER BY profit DESC
  • 您的查询有UNION,但我希望不需要从存档表中重复删除,因此我将其更改为UNION ALL - 这更快,因为它不会删除重复项
  • 根据您提供的内容,您有SELECT ORDERS.*SELECT ORDER_ARCHIVE.*但从未使用任何列。
  • 聚合函数(SUM)全部在TBLITEM表中,这在派生表/内联视图中是不必要的。
  • 我省略了USE KEY(finalized);如果您愿意,可以重新添加它,但我会与之进行比较 - 我建议在运行查询之前在两个表上偶尔运行ANALYZE TABLE,以便优化程序具有相对较新的统计信息。
  • 我在finalized列的索引中看不到多少值,但我不知道您的数据或使用 - 只是这个查询。但基于此查询,我将索引:

    1. ORDER_ID
    2. order_time
    3. 完成
    4. ...作为覆盖索引 - 按提供的顺序排列有三列的单个索引,因为顺序在覆盖索引中很重要。

答案 2 :(得分:0)

我根据你的帮助将其重写如下,并为tblOrder和tblOrder存档添加了推荐的覆盖索引,事情看起来要快得多。但是我仍然想知道你的编写方式是否还有更多...但我还需要使用tblItem_archive加入tblOrder_archive。

SELECT
  description_invoice
, supplier
, type
, sum(quantity) AS num_sold
, sum(quantity*wholesale) AS wholesale_price
, sum(quantity*price) AS retail_price
, sum(quantity*price) - sum(quantity*wholesale) AS profit
FROM (
    SELECT
      tblOrder.order_id
    , tblItem.description_invoice
    , tblItem.type
    , tblItem.product_number
    , tblItem.quantity
    , tblItem.wholesale
    , tblItem.price
    , tblItem.supplier 
    FROM tblOrder, tblItem
    WHERE
      tblItem.order_id = tblOrder.order_id 
     AND
      finalized=1
     AND
      wholesale <> 0
     AND (order_time >= 1251788400 AND order_time <= 1283669999) 

   UNION ALL

    SELECT
      tblOrder_archive.order_id
    , tblItem_archive.description_invoice
    , tblItem_archive.type
    , tblItem_archive.product_number
    , tblItem_archive.quantity
    , tblItem_archive.wholesale
    , tblItem_archive.price
    , tblItem_archive.supplier
    FROM tblOrder_archive, tblItem_archive
    WHERE
      tblItem_archive.order_id=tblOrder_archive.order_id
     AND
      finalized=1
     AND
      wholesale <> 0
     AND (order_time >= 1251788400 AND order_time <= 1283669999)
) AS main_table
GROUP BY
  description_invoice
, supplier,type 
ORDER BY profit DESC;