简体   繁体   中英

Query is taking too much time

I want to take your ideas regarding the below query:

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);

This query is taking so much time even though in the expense_data table, there are four indices:

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

I do not know why it takes so much time is it because of two much join or is it because too much indeces. Can you please suggest?

Could be that your where clause contains a logical error. Look at the last line (last OR condition):

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 

That means "take records between dates etc. AND with status 0, OR take all records with status 2 "

To speed things up, you might want to try to make a combined index on a combination of the columns you join on. That index might be more useful than the four separate indexes you have now, although you can still keep those. Apart from those four fields, you may even want to experiment by adding status and/or expense_data to the index.

Recoding it to make the joins clearer (and removing that massive join where if expense status was all tables were joined) gives:-

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);

Looking at that it is important that you have an index on supplier_code on the supplier_data table, an index on expense_counter on the expense_item table and an index on account_code on the tree_data table.

I suspect you don't really want to return items with an expense status of 0 and an expense date in that range, with any record of expense status 2 irrespective of date so the following might be what you want:-

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);

You have to put OR condition in brackets:

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; 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM