Question

I'm working on some queries for a landing page. This page will have a variety of search options for the user. The queries will be constructed from various pieces depending on what the user has selected and fired off to a DB2 database. All in all, there are over 100 unique queries. I'm working on getting performance down and I have one that is taking a bit too long. The basic structure is thus:

SELECT ...
FROM
TABLE A
--A few joins and a few left joins--
WHERE A.FIELD1 IS NOT NULL
  AND A.FIELD2 IN (:parameter) --No more than two values in here
  AND (
        A.FIELD3 IN ('ONE', 'TWO')
    OR (A.FIELD3 IN ('THREE','FOUR') AND A.FIELD4 BETWEEN :x AND :y)
  )
  AND A.FIELD5 IN (uncorrelated subquery, potentially returns over 1k values, usually less)

FIELD2 and 3 are selected while the others are only filtered. FIELD5 is used in a join, but that's not related to the filtering with the subquery. The problem is coming from the (X OR Y) clause. The query is taking about 3 seconds to execute as it is now. If I remove either of the conditions in the OR clause, it executes in less than a tenth of the time. Oddly enough, removing them both puts it back around 3 seconds, which doesn't make much sense as it doesn't seem to increase the size of the dataset that much. The explain plan with the OR clause or without both conditions is nearly identical, but it doesn't appear to be an index issue as it seems to be hitting the same indexes on relevant tables. The big cost is coming from the outermost NLJOIN in both cases. Remove one condition from the OR clause (so it's just an AND) and the explain plan changes significantly, lowering the cost bigtime and changing the structure significantly, although the same relevant indexes are used.

I've tried working around this with subqueries and unions or even using a UNION ALL between two queries that differentiate only on that clause (which did help this instance a little bit, but slowed down other queries significantly), but nothing seems to help the execution time. I can't really post the full details as the query is pretty massive, but hopefully this is enough to get the idea across. I know that OR clauses can sometimes throw the optimizer for a loop, so I guess general advice on avoiding problematic OR clauses or nudging the optimizer into a better direction would be greatly appreciated, even if it's not directly specific to this example.

Était-ce utile?

La solution

you could try with a case statement instead.

Instead of

AND ( A.FIELD3 IN ('ONE', 'TWO') 
 OR (A.FIELD3 IN ('THREE','FOUR') AND A.FIELD4 BETWEEN :x AND :y))

Use

AND CASE WHEN A.FIELD3 IN ('ONE', 'TWO') THEN 1 
     WHEN A.FIELD3 IN ('THREE','FOUR') AND A.FIELD4 BETWEEN :x AND :y THEN 1 ELSE 0 END = 1

Autres conseils

it may seem extreme, but you could union two fast versions together... one with this line

AND A.FIELD3 IN ('ONE', 'TWO') 

and one with

A.FIELD3 IN ('THREE','FOUR') AND A.FIELD4 BETWEEN :x AND :y)

Try:

SELECT ...
FROM
TABLE A
--A few joins and a few left joins--
WHERE A.FIELD1 IS NOT NULL
  AND A.FIELD2 IN (:parameter) 
  AND A.FIELD3 IN ('ONE', 'TWO', 'THREE', 'FOUR')
  AND (A.FIELD3 IN ('ONE', 'TWO') OR A.FIELD4 BETWEEN :x AND :y)
  AND A.FIELD5 IN (uncorrelated subquery)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top