RE: SQL help

  • From: "Sweetser, Joe" <JSweetser@xxxxxxxx>
  • To: Jonathan Lewis <jonathan@xxxxxxxxxxxxxxxxxx>, Jeffrey Beckstrom <JBECKSTROM@xxxxxxxxx>
  • Date: Tue, 13 May 2014 14:41:36 +0000

Wow, wow, wow.  I am old enough nowadays to have the belief I have seen it all 
(at least, in  theory) but this suggestion/answer by Mr. Lewis was truly 
eye-popping.  The developer I have been working with had the same reaction (but 
he's much younger so that's to be expected :) ).  Taking Jonathan's suggestion 
practically verbatim and making a few other minor tweaks like breaking one 
delete statement into 2 separate ones, the run time for this little program 
went from over 12 hours (and not even finishing) to about 2.5 minutes.   The 
buffer gets that were over a billion for this particular delete dropped to 2.8 
million.

SSS3@idm3> @AdditionalInterestMigration.pls

PL/SQL procedure successfully completed.

Elapsed: 00:02:27.58
SSS3@idm3>

It's been said many times over the years but I will say it again:  This list is 
quite possibly the single best place on the internet to receive timely, 
accurate, and valuable information about this little beast we call Oracle.

I have posted the explain plan for the revised statement below.  Note the 
overall cost dropped from 1096 to 13 (!) with Jonathan's insight.  I just may 
have to take my "Oracle Core - Essential Internals for DBAs and Developers" 
book next time JL visits Colorado in order to have it signed.  I thought my 
groupie days were over, but perhaps not.    :)

Thanks again for the advice.
-joe

SQL stmt
DELETE
FROM BLDGASSIGNEDADDINT
WHERE ID_ADDITIONALINTEREST in
   (
   SELECT pk
   FROM ADDINTEREST
   WHERE ID_INSURED = :1
   UNION ALL
   SELECT pk
   FROM ADDINTEREST
   WHERE lnnvl(ID_INSURED = :2)
  AND ID_POLICY in
      (
      SELECT q.pk
      FROM quote q
      WHERE q.COMMONINSURED_ID = :3
     AND q.ENDINFO_ID is null
      )
  AND id_policy != :4
   )

Explain plan
DELETE STATEMENT Optimizer=ALL_ROWS (Cost=13 Cardinality=6 Bytes=144)
DELETE OF BLDGASSIGNEDADDINT
  NESTED LOOPS (Cost=13 Cardinality=6 Bytes=144)
   VIEW OF VW_NSO_1 (VIEW) (Cost=10 Cardinality=3 Bytes=39)
    SORT (UNIQUE) (Cost=10 Cardinality=3 Bytes=62)
     UNION-ALL
      TABLE ACCESS (BY INDEX ROWID) OF ADDINTEREST (TABLE) (Cost=2 
Cardinality=1 Bytes=8)
       INDEX (RANGE SCAN) OF ADDINTERESTINSURED (INDEX) (Cost=1 Cardinality=1)
      NESTED LOOPS
       NESTED LOOPS (Cost=8 Cardinality=2 Bytes=54)
        TABLE ACCESS (BY INDEX ROWID) OF QUOTE (TABLE) (Cost=5 Cardinality=2 
Bytes=28)
         INDEX (RANGE SCAN) OF QUOTE_COMN_INSRD_ID_IDX (INDEX) (Cost=3 
Cardinality=2)
        INDEX (RANGE SCAN) OF ADDINTERESTPOLICY (INDEX) (Cost=1 Cardinality=2)
       TABLE ACCESS (BY INDEX ROWID) OF ADDINTEREST (TABLE) (Cost=2 
Cardinality=1 Bytes=13)
   INDEX (RANGE SCAN) OF BLDGASSIGNEDADDINT_BYADDINT (INDEX) (Cost=1 
Cardinality=2 Bytes=22)

From: Jonathan Lewis [mailto:jonathan@xxxxxxxxxxxxxxxxxx]
Sent: Monday, May 12, 2014 2:00 PM
To: Sweetser, Joe; Jeffrey Beckstrom
Cc: ORACLE-L
Subject: RE: SQL help



Looks like an example of "subquery with OR" that forces a filter operation and 
stops the optimiser from doing an unnest. See: 
http://jonathanlewis.wordpress.com/2007/02/26/subquery-with-or


I think you'll probably have to rewrite the query to look something like:

delete from  BLDGASSIGNEDADDINT where ID_ADDITIONALINTEREST in
(select pk from ADDINTEREST where ID_INSURED = :3
union all
select pk from ADDINTEREST where lnnvl(ID_INSURED = :3)
and ID_POLICY in
    (select q.pk from quote q where q.COMMONINSURED_ID = :1 and q.ENDINFO_ID is 
null)
and id_policy != :2
);


I think you'll also need an index on BLDGASSIGNEDADDINT(ID_ADDITIONALINTEREST) 
since that's how you're trying to identify the rows for deletion.



Regards
Jonathan Lewis
http://jonathanlewis.wordpress.com
@jloracle
________________________________
From: oracle-l-bounce@xxxxxxxxxxxxx<mailto:oracle-l-bounce@xxxxxxxxxxxxx> 
[oracle-l-bounce@xxxxxxxxxxxxx] on behalf of Sweetser, Joe [JSweetser@xxxxxxxx]
Sent: 12 May 2014 20:39
To: Jeffrey Beckstrom
Cc: ORACLE-L
Subject: RE: SQL help
Forgot the index definitions....
There are indexes on:
addinterest.pk, addinterest.id_insured, addinterest.id_policy
and
bldgassignedaddint.id_additionalinterest

-joe
From: Jeffrey Beckstrom [mailto:JBECKSTROM@xxxxxxxxx]
Sent: Monday, May 12, 2014 1:07 PM
To: Sweetser, Joe
Subject: Re: SQL help

Where's the index on id_policy for addinterest?

>>> "Sweetser, Joe" <JSweetser@xxxxxxxx<mailto:JSweetser@xxxxxxxx>> 5/12/14 
>>> 3:01 PM >>>
All,

Without going into too much detail as to why (at this point), I have the 
following statement:

delete from  BLDGASSIGNEDADDINT where ID_ADDITIONALINTEREST in
(select pk from ADDINTEREST where (ID_POLICY in
(select q.pk from quote q where q.COMMONINSURED_ID = :1 and q.ENDINFO_ID is 
null) and id_policy != :2)
or ID_INSURED = :3);

That yields an explain plan of this:

DELETE STATEMENT Optimizer=ALL_ROWS (Cost=1096 Cardinality=2316 Bytes=150540)
DELETE OF BLDGASSIGNEDADDINT
  FILTER
   HASH JOIN (Cost=1096 Cardinality=185659 Bytes=12067835)
    TABLE ACCESS (FULL) OF ADDINTEREST (TABLE) (Cost=435 Cardinality=88384 
Bytes=3446976)
    TABLE ACCESS (FULL) OF BLDGASSIGNEDADDINT (TABLE) (Cost=110 
Cardinality=185657 Bytes=4827082)
   TABLE ACCESS (BY INDEX ROWID) OF QUOTE (TABLE) (Cost=3 Cardinality=1 
Bytes=39)
    INDEX (UNIQUE SCAN) OF SYS_C005688632 (INDEX (UNIQUE)) (Cost=2 
Cardinality=1)

That gets executed many times in a loop and is doing a boatload of logical IO's 
and no physical IO's.  By boatload I mean over 100 million buffer gets each 
hour.

I am trying to figure out a way to avoid the full table scans.  Below are the 
table creation statements for bldgassignaddint and addinterest tables minus 
extraneous columns

CREATE TABLE "BLDGASSIGNEDADDINT"
( "ID_BUILDING" NUMBER(10),
"ID_ADDITIONALINTEREST" NUMBER(10),
CONSTRAINT "FK17A3EAC98DDD8F2C" FOREIGN KEY ("ID_ADDITIONALINTEREST") 
REFERENCES "ADDINTEREST" ("PK") VALIDATE ,
CONSTRAINT "FK17A3EAC9C50C5C52" FOREIGN KEY ("ID_BUILDING") REFERENCES 
"BUILDING" ("PK") VALIDATE ,
PRIMARY KEY ("ID_BUILDING", "ID_ADDITIONALINTEREST") VALIDATE;

CREATE TABLE "ADDINTEREST"
( "PK" NUMBER(10),
"ID_INSURED" NUMBER(10),
"ID_POLICY" NUMBER(10),
CONSTRAINT "FK4B56FACB277D71C9" FOREIGN KEY ("ID_INSURED") REFERENCES "INSURED" 
("PK") VALIDATE ,
CONSTRAINT "FK4B56FACBE57638AB" FOREIGN KEY ("ID_POLICY") REFERENCES "QUOTE" 
("PK") VALIDATE ,
PRIMARY KEY ("PK") VALIDATE );

Any suggestions?  I am having no luck at all trying to incorporate EXISTS 
instead of IN.

Thanks,
-joe
Confidentiality Note: This message contains information that may be 
confidential and/or privileged. If you are not the intended recipient, you 
should not use, copy, disclose, distribute or take any action based on this 
message. If you have received this message in error, please advise the sender 
immediately by reply email and delete this message. Although ICAT Managers, 
LLC, Underwriters at Lloyd's, Syndicate 4242, scans e-mail and attachments for 
viruses, it does not guarantee that either are virus-free and accepts no 
liability for any damage sustained as a result of viruses. Thank you.
--
//www.freelists.org/webpage/oracle-l





.

Other related posts: