tags:

views:

118

answers:

3

I have this SQL query but its running soo slow,

SELECT 
    wr.wr_re_id as ReID, 
    sum(wr.wr_total) as PRTotal 
FROM 
    Workorder2 w
    INNER JOIN 
    Workorder_Row2 wr ON wr.wr_wo_id = w.wo_id
WHERE 
    (w.wo_type = 1 or w.wo_type = 2)
    AND wr.wr_row_type = 2
    AND w.wo_lo_id like '%'
    AND w.wo_date_time >= '2010-01-01' 
    AND w.wo_date_time <= '2010-01-31'
    AND wr.wr_wo_id IN
        (SELECT 
            wr2.wr_wo_id 
        FROM 
            Workorder_Row2 wr2 
            INNER JOIN Workorder2 w2 ON w2.wo_id = wr2.wr_wo_id 
            AND w2.wo_date_time >= '2010-01-01' 
            AND w2.wo_date_time <= '2010-01-31' 
        WHERE 
            wr2.wr_row_type = 1)
GROUP BY 
    wr.wr_re_id

any advice how I can speed it up? it takes almost 1min to execute.
I think the problem is with the AND wr.wr_wo_id IN (SELECT ... but i need this to know if there are product sales on the same workorder that contains a threatment.

+1  A: 

If you are using MySQL, you can use EXPLAIN to get an idea why your query is slow. See http://dev.mysql.com/doc/refman/5.1/en/explain.html for more information about EXPLAIN syntax.

Daniel M.
+2  A: 

Firstly, have a look at your execution plan. It's hard for us to optimize it as we don't know your data.

The thing that jumps out the most is the wr.wr_wo_id IN (SELECT...) part. This would be far more efficient as a join, like this:

SELECT wr.wr_re_id as ReID, sum(wr.wr_total) as PRTotal 
FROM Workorder2 w
    INNER JOIN Workorder_Row2 wr on wr.wr_wo_id = w.wo_id
    INNER JOIN (
        SELECT DISTINCT wr2.wr_wo_id
        FROM Workorder_Row2 wr2 
            INNER JOIN Workorder2 w2 on w2.wo_id = wr2.wr_wo_id 
        WHERE w2.wo_date_time >= '2010-01-01' 
            AND w2.wo_date_time <= '2010-01-31'
            AND wr2.wr_row_type = 1
    ) T ON T.wr_wo_id = wr.wr_wo_id
WHERE (w.wo_type = 1 or w.wo_type = 2)
    AND wr.wr_row_type = 2
    AND w.wo_lo_id like '%'
    AND w.wo_date_time >= '2010-01-01' AND w.wo_date_time <= '2010-01-31'
GROUP BY wr.wr_re_id

It is worth considering whether it would help to add indices. It depends on how often you update/insert into/delete from those tables, and how selective each column is.

ADDITIONAL:

To do the reverse, i.e. to replace WHERE wr.wr_wo_id NOT IN (SELECT...), you would use:

SELECT wr.wr_re_id as ReID, sum(wr.wr_total) as PRTotal 
FROM Workorder2 w
    INNER JOIN Workorder_Row2 wr on wr.wr_wo_id = w.wo_id
    LEFT JOIN (
        SELECT DISTINCT wr2.wr_wo_id
        FROM Workorder_Row2 wr2 
            INNER JOIN Workorder2 w2 on w2.wo_id = wr2.wr_wo_id 
        WHERE w2.wo_date_time >= '2010-01-01' 
            AND w2.wo_date_time <= '2010-01-31'
            AND wr2.wr_row_type = 1
    ) T ON T.wr_wo_id = wr.wr_wo_id
WHERE T.wr2.wr_wo_id IS NULL
    AND (w.wo_type = 1 or w.wo_type = 2)
    AND wr.wr_row_type = 2
    AND w.wo_lo_id like '%'
    AND w.wo_date_time >= '2010-01-01' AND w.wo_date_time <= '2010-01-31'
GROUP BY wr.wr_re_id

However, it is more readable and (I would guess, though try it) more efficient to use:

SELECT wr.wr_re_id as ReID, sum(wr.wr_total) as PRTotal 
FROM Workorder2 w
    INNER JOIN Workorder_Row2 wr on wr.wr_wo_id = w.wo_id
WHERE (w.wo_type = 1 or w.wo_type = 2)
    AND wr.wr_row_type = 2
    AND w.wo_lo_id like '%'
    AND w.wo_date_time >= '2010-01-01' AND w.wo_date_time <= '2010-01-31'
    AND NOT EXISTS (
        SELECT NULL
        FROM Workorder_Row2 wr2 
            INNER JOIN Workorder2 w2 on w2.wo_id = wr2.wr_wo_id 
        WHERE w2.wo_date_time >= '2010-01-01' 
            AND w2.wo_date_time <= '2010-01-31'
            AND wr2.wr_row_type = 1
            AND wr2.wr_wo_id = wr.wr_wo_id
    )
GROUP BY wr.wr_re_id
Paul
Thanks! this helped alot! its down to 0sec (: i just found out about the execution plan sorry for not including that. i have the same issue with wr.wr_wo_id NOT IN (SELECT..). would this the same as: ) T ON T.wr_wo_id <> wr.wr_wo_id
Alexander
sorry for jumping to conclusions! but this was not the correct answer.the query above does not return the same result as when i use the IN (SELECT...) statement.
Alexander
Are you sure? I might be missing something obvious, but they look equivalent to me. In what respect do the results differ?
Paul
the end result i'm looking for is how much Product sales together with a Service are their, grouped by wr_re_id.Workorder Contains a receipt header, the Workorder_Row table contains the receipt rows.there are 2 types of rows. (1) Service sales and (2) Product sales.select * from workorder w inner join workorder_row2 wr on wr.wr_wo_id = w.wo_idWHERE wr.wr_row_type = 2shows all workorders that has got product sales.and wr.wr_wo_id IN (SELECT wr2.wr_wo_id FROM Workorder_row wr2 WHERE wr2.wr_row_type = 1).should give me only the workorders where a Service is sold as well
Alexander
its probably me missing something obvious! i bet you are a lot! sharper on SQL then me
Alexander
Found a potential flaw...I've added a DISTINCT. When this was missing, then there would be duplicate rows, and the PRTotals would get too large.
Paul
Exactly! this solved it! thank you very much!!a follow up question that still remained tho S:how could i reverse this, if i want to find out product sales without a service in the same receipt. before i used NOT in, i tried T ON T.wr_wo_id <> wr.wr_wo_id but that was obviously not right ):
Alexander
I'll add it to the answer above.
Paul
sorry for the conclusion jumping again! when i added the DISTINCT to the query its back to 20 seconds! madness... its still faster then IN (SELECT ...) but slow.anyway i will investigate if i cant run with this and just add a stupid progress-bar to my application :P
Alexander
Just for completeness, try that last query, but with `AND NOT EXISTS` changed to `AND EXISTS`. Also, did you look to see whether there are any indices you could try? I assume both tables have a primary key? Beyond that you could try: `CREATE INDEX Workorder2_IX ON Workorder2 (wo_date_time, wr_wo_id)` and `CREATE INDEX Workorder_Row2_IX ON Workorder_Row2 (wr_row_type, wr_wo_id)` It depends a lot on your data.
Paul
first query took 15 sec to execute, the 2nd one 14sec (: I can say my final result ended up with a database change and program change. I figured out i needed to save a lot more data for my statistics report to be correct. with this i then could rebuild my query and its now running really smooth. i learned alot by this and thank you all for your time!
Alexander
A: 

Every SQL datbase engine has a query analyzer that will help you figure out why it's running slow. But in this case the solution is probably as simple as creating indexes on these columns:

  • Workorder2.wo_id
  • Workorder_Row2.wr_wo_id

Try adding these indexes as clustered indexes. If that doesn't work check the query analyzer for further ideas.

  • In SQL Server this is found in the Management Studio by entering your query in the query window and clicking on the buttons "Display Estimated Execution Plan" and "Analyze Query in Database Tuning Advisor"
  • In MySQL you can use the EXPLAIN command

Note: Don't worry about your "IN (SELECT ...)" section. It will be just as efficient as a join on any modern database engine.

RobC
Thanks RobC, i will look into that. this would requirer a database change of some sort i guess?
Alexander