【问题标题】:Query is taking too much time查询花费了太多时间
【发布时间】:2013-07-11 19:39:54
【问题描述】:

我想听听您对以下查询的看法:

select a.expense_code, a.expense_date, a.expense_supplier_code, b.supplier_name, a.expense_discount, a.expense_payment_method, a.expense_payment_transfer_to, a.expense_advance, a.expense_status,
                            sum(c.expense_item_buy_price * c.expense_item_quantity) , d.account_name, a.expense_counter, a.expense_type, a.expense_saving_type, a.expense_payment_transfer_from
                            from expense_data a, supplier_data b, expense_item c, tree_data d
                            where a.expense_supplier_code = b.supplier_code and a.expense_payment_transfer_to= d.account_code
                            and a.expense_counter = c.expense_counter
                            and a.expense_date between '2013-01-01' and '2014-01-01' and a.expense_status = 0 or a.expense_status = 2 group by (a.expense_counter);

即使在expense_data 表中有四个索引,此查询也花费了很多时间:

1- Expense_code. 
2- expense_user_id
3- expense_supplier_code
4- expense_payment_transfer_from

我不知道为什么要花这么多时间是因为两个太多的加入还是因为太多的indeces。你能建议吗?

【问题讨论】:

  • 你看过运算符优先级吗?并且具有比 or 更高的优先级。最后,你写a.expense_status = 0 or a.expense_status = 2。我认为这应该放在括号中,或者您应该将其更改为 a.expense_status in (0, 2),除非您实际上的意思是应该返回任何状态为 2 的行,而不管其他条件如何。
  • 运行'EXPLAIN EXTENDED'+ YourQuery Code;并将结果发布到您上面的问题中,否则我们不知道表的大小、类型、索引用于执行查询的方式等。
  • 别忘了使用执行计划。

标签: mysql sql


【解决方案1】:

重新编码以使连接更清晰(并删除如果费用状态是所有表都已连接的大量连接)给出:-

SELECT a.expense_code, 
    a.expense_date, 
    a.expense_supplier_code, 
    b.supplier_name, 
    a.expense_discount, 
    a.expense_payment_method, 
    a.expense_payment_transfer_to, 
    a.expense_advance, 
    a.expense_status,
    SUM(c.expense_item_buy_price * c.expense_item_quantity) , 
    d.account_name, 
    a.expense_counter, 
    a.expense_type, 
    a.expense_saving_type, 
    a.expense_payment_transfer_from
FROM expense_data a, 
INNER JOIN supplier_data b ON a.expense_supplier_code = b.supplier_code
INNER JOIN expense_item c ON a.expense_counter = c.expense_counter
INNER JOIN tree_data d ON a.expense_payment_transfer_to= d.account_code
WHERE a.expense_date BETWEEN '2013-01-01' AND '2014-01-01' 
AND a.expense_status = 0 OR a.expense_status = 2 
GROUP BY (a.expense_counter);

请注意,supper_data 表上的supplier_code 索引、expense_item 表上的expense_counter 索引和tree_data 表上account_code 的索引是很重要的。

我怀疑您真的不想退回费用状态为 0 且费用日期在该范围内的项目,无论日期如何,费用状态为 2 的任何记录都可能是您想要的:-

SELECT a.expense_code, 
    a.expense_date, 
    a.expense_supplier_code, 
    b.supplier_name, 
    a.expense_discount, 
    a.expense_payment_method, 
    a.expense_payment_transfer_to, 
    a.expense_advance, 
    a.expense_status,
    SUM(c.expense_item_buy_price * c.expense_item_quantity) , 
    d.account_name, 
    a.expense_counter, 
    a.expense_type, 
    a.expense_saving_type, 
    a.expense_payment_transfer_from
FROM expense_data a, 
INNER JOIN supplier_data b ON a.expense_supplier_code = b.supplier_code
INNER JOIN expense_item c ON a.expense_counter = c.expense_counter
INNER JOIN tree_data d ON a.expense_payment_transfer_to= d.account_code
WHERE a.expense_date BETWEEN '2013-01-01' AND '2014-01-01' 
AND a.expense_status IN (0, 2)
GROUP BY (a.expense_counter);

【讨论】:

    【解决方案2】:

    可能是您的 where 子句包含逻辑错误。 看最后一行(最后一个 OR 条件):

    where 
      ....
      and a.expense_counter = c.expense_counter 
      and a.expense_date BETWEEN '2013-01-01' AND '2014-01-01' 
      and a.expense_status = 0 
      or a.expense_status = 2 
    

    这意味着“在日期等之间获取记录,并且状态为 0,或者获取所有状态为 2 的记录

    【讨论】:

    • 欢迎来到 Stack Overflow。当某人的回答证明对您有帮助时,请单击绿色轮廓复选标记接受它。为您认为有用的任何答案投票也是有礼貌的。
    【解决方案3】:

    为了加快速度,您可能希望尝试在您加入的列的组合上创建组合索引。该索引可能比您现在拥有的四个独立索引更有用,尽管您仍然可以保留它们。除了这四个字段之外,您甚至可能想通过将status 和/或expense_data 添加到索引来进行试验。

    【讨论】:

    • 他需要为表格发布扩展说明和描述,即使覆盖索引也是毫无意义的,并且如果它们的排序不正确或类型/长度有差异,则会浪费空间/时间。
    【解决方案4】:

    您必须将 OR 条件放在括号中:

    SELECT a.expense_code, 
       a.expense_date, 
       a.expense_supplier_code, 
       b.supplier_name, 
       a.expense_discount, 
       a.expense_payment_method, 
       a.expense_payment_transfer_to, 
       a.expense_advance, 
       a.expense_status, 
       SUM(c.expense_item_buy_price * c.expense_item_quantity), 
       d.account_name, 
       a.expense_counter, 
       a.expense_type, 
       a.expense_saving_type, 
       a.expense_payment_transfer_from 
    FROM expense_data a, 
       supplier_data b, 
       expense_item c, 
       tree_data d 
    WHERE a.expense_supplier_code = b.supplier_code 
       AND a.expense_payment_transfer_to = d.account_code 
       AND a.expense_counter = c.expense_counter 
       AND a.expense_date BETWEEN '2013-01-01' AND '2014-01-01' 
       AND (a.expense_status = 0 OR a.expense_status = 2)
    GROUP BY a.expense_counter; 
    

    【讨论】:

    • 这是一种功能上的改变,它改变了行为而不是加快速度(尽管它可能会意外地)。
    • 它会加快速度 - 通过删除这个巨大的笛卡尔加入 AxBxCxD!这绝对是一个错误,不是故意的。
    • 是的,但通常不希望通过更改结果来加快速度,除非它实际上是一个错误,目前这只是一个猜测。您至少应该提及您更改了查询的行为。
    • 这是一个明显的逻辑错误我的朋友。并且彻底修复它会改变查询的行为 - 通过使其工作。
    • 非常好的建议非常感谢
    猜你喜欢
    • 2018-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-04
    • 2012-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多