RE: Optimizing a SQL statement

  • From: "Hameed, Amir" <Amir.Hameed@xxxxxxxxx>
  • To: "jonathan@xxxxxxxxxxxxxxxxxx" <jonathan@xxxxxxxxxxxxxxxxxx>, "oracle-l@xxxxxxxxxxxxx" <oracle-l@xxxxxxxxxxxxx>
  • Date: Fri, 6 Dec 2019 12:36:14 +0000

Thank you everyone for your comments. This query belongs to standard code of an 
ERP package called IFS. I have found a few other similar statements that are 
written the same way. We have notified the vendor and asked to fix them.

Thanks
-----Original Message-----
From: oracle-l-bounce@xxxxxxxxxxxxx <oracle-l-bounce@xxxxxxxxxxxxx> On Behalf 
Of Jonathan Lewis
Sent: Friday, December 6, 2019 6:45 AM
To: oracle-l@xxxxxxxxxxxxx
Subject: Re: Optimizing a SQL statement


As Andy indicates - this looks like a "programmer-friendly" implementation of a 
search screen that allows the end user to supply a choice of search values. 
Instead of coding to produce SQL that gives the optimizer the best possible 
chance of finding a good plan it uses the same fixed SQL supplying nulls as 
input parameters where the end-user has not supplied a value.

Given the long list of predicates, and the need to produce a single plan that 
will ALWAYS give the correct result, the optimizer has only two options:  drive 
through the product_id (which I assume is the PK so not null) to check every 
row in the table as this will produce the result in the correct order, or do a 
tablescan to check every row, then sort.

As Sayan says - the only other option you have is to ensure that EVERY column 
used in the WHERE clause has its own index so that the optimizer can choose to 
do concatenation (11g) or OR-expansion (12.2)  (See: 
https://jonathanlewis.wordpress.com/2018/03/02/conditional-sql-5/ ;). Then, 
given the number of OR'ed predicates and the way the optimizer treats all the 
"LIKE with bind" predicates you may still have to include a use_concat() hint 
to force concatenation.

If you do generate a complete concatenated path, check the OUTLINE section and 
copy the full use_concat() hint from there back to the code.  Don't be 
surprised, by the way, by the number of LNNVL() predicates that appear if 
concatenation takes place.

You may also find that you can further improve performance by querying the 
product table only, sorting and limiting to 500 rows, before joining to the 
place table - but that would only make a difference in cases where the number 
of rows initially found in the product table was significantly greater than 500.

Regards
Jonathan Lewis

________________________________________
From: oracle-l-bounce@xxxxxxxxxxxxx <oracle-l-bounce@xxxxxxxxxxxxx> on behalf 
of Hameed, Amir <Amir.Hameed@xxxxxxxxx>
Sent: 04 December 2019 17:05
To: oracle-l@xxxxxxxxxxxxx
Subject: Optimizing a SQL statement

Hi,
The following statement does over 4.5 million LIOs per execution. It runs at an 
average of 3-4 times per minute. Almost all of the columns in the statement are 
indexed. Tables PRODUCT and PLACE have 4,576,690 and 1,892,243 rows 
respectively. The only way I have been able to optimize it is to add a hint to 
scan the PRODUCT table in PARALLEL, which does help with the elapsed time and 
the statement finishes within 10 seconds. However, because the statement runs 
quite frequently, I don't believe using the parallel hint would be a good idea. 
What would be a good approach I should look into to help optimize it. The DB 
version is 11.2.0.4.

Thanks

SELECT * FROM ( SELECT place.name,place.place_id FROM  product   LEFT
OUTER JOIN place  ON  product.place_id_owned_by = place.place_id WHERE 
((((((((((((((((((((( product.product_id LIKE :p1 OR product.model_id LIKE :p2) 
OR product.part_id LIKE :p3) OR product.serial_id LIKE :p4) OR 
product.serial_id LIKE :p5) OR product.serial_id LIKE :p6) OR product.serial_id 
LIKE :p7) OR product.place_id_owned_by LIKE :p8) OR product.place_id LIKE :p9) 
OR product.temporary_serial LIKE :p10) OR product.temporary_serial LIKE :p11) 
OR product.temporary_serial LIKE :p12) OR product.temporary_serial LIKE :p13) 
OR product.condition_code LIKE :p14) OR product.lot_id LIKE :p15) OR 
product.lot_id LIKE :p16) OR product.lot_id LIKE :p17) OR product.lot_id LIKE 
:p18) OR product.asset_id LIKE :p19) OR product.asset_id LIKE :p20) OR 
product.asset_id LIKE :p21) OR product.asset_id LIKE :p22) ORDER BY
product.product_id ASC   )
WHERE ROWNUM <= 500
;

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.01       0.01          0          0          0           0
Fetch        2     57.02      72.37     200413    4570892          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4     57.04      72.39     200413    4570892          0           1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  
---------------------------------------------------
         1          1          1  COUNT STOPKEY (cr=4570892 pr=200413 pw=0 
time=69235827 us)
         1          1          1   VIEW  (cr=4570892 pr=200413 pw=0 
time=69235818 us cost=1752 size=82164 card=501)
         1          1          1    NESTED LOOPS OUTER (cr=4570892 pr=200413 
pw=0 time=69235815 us cost=1752 size=148797 card=501)
         1          1          1     TABLE ACCESS BY INDEX ROWID PRODUCT 
(cr=4570888 pr=200411 pw=0 time=72376638 us cost=1003 size=511287210 
card=2157330)
   4578439    4578439    4578439      INDEX FULL SCAN PRODUCTP1 (cr=20522 
pr=19831 pw=0 time=18481568 us cost=7 size=0 card=1063)(object id 2080845)
         1          1          1     TABLE ACCESS BY INDEX ROWID PLACE (cr=4 
pr=2 pw=0 time=243 us cost=2 size=60 card=1)
         1          1          1      INDEX UNIQUE SCAN PLACEP1 (cr=3 pr=1 pw=0 
time=146 us cost=1 size=0 card=1)(object id 2080816)

--
//www.freelists.org/webpage/oracle-l


--
//www.freelists.org/webpage/oracle-l


Other related posts: