Search code examples
mysqlsqlperformancequery-optimizationquery-performance

Mysql OR performance Issue


I am using OR in mysql query to check different values at same time, I found results are coming very slow and here I am joining two tables also, I am unable to check which is taking long time.

Query here:

select l.*, l.category as lid
FROM listings AS l
LEFT JOIN listprices as pr ON pr.product_id=l.id 
where (pr.price >= 100 AND pr.price <= 1000) 
  OR (pr.price >= 1001 AND pr.price <= 5000) 
OR  (pr.price >= 5001 AND pr.price <= 10000) 
 order by pr.price ASC

query working fine but results taking too much time. If any other alternative to get fast. I have heard OR taking much time to execute results.


Solution

  • MySQL doesn't handle OR clauses very well. (Correctly, but slowly.) In your case it seems like the presence of your sequence of OR clauses is defeating the use of your index on listprices.price. That will make your performance stink. Let's try to refactor this query to avoid that sequence of OR clauses.

    Notice that you have a LEFT JOIN x ... WHERE x.column ... pattern in your query. That converts your LEFT JOIN to an inner JOIN.

    First, let's do a subquery to get a list of listprices.product_id values with matching prices. That goes like this:

    SELECT price, product_id FROM listprices pr WHERE pr.price >= 100 AND pr.price <= 1000
    UNION
    SELECT price, product_id FROM listprices pr WHERE pr.price >= 1001 AND pr.price <= 5000
    UNION
    SELECT price, product_id FROM listprices pr WHERE pr.price >= 5001 AND pr.price <= 10000
    

    These three queries (unioned together) each can be made very fast by a compound index on price, product_id. You'd be wise to test this query to ensure it's giving the result you want.

    Then you will use this query as a subquery; that is, as if it were itself a table:

    select l.*, l.category as lid
      FROM listings AS l
      JOIN (
           SELECT price, product_id FROM listprices pr WHERE pr.price >= 100 AND pr.price <= 1000
           UNION
           SELECT price, product_id FROM listprices pr WHERE pr.price >= 1001 AND pr.price <= 5000
           UNION
           SELECT price, product_id FROM listprices pr WHERE pr.price >= 5001 AND pr.price <= 10000
           ) pr ON pr.product_id = l.id
     order by pr.price ASC
    

    This should get you the result you want more quickly.