简体   繁体   中英

How to avoid same subquery in multiple select in oracle?

This is three different selects using same subquery. How can I use subquery result instead of doing sub query again.

SELECT  *
FROM    Address
WHERE   address_key IN
        (
        SELECT  address_key
        FROM    person_address
        WHERE   peson_key IN (person_list)
        );   -- person_list := '1,2,3,4'

SELECT  *
FROM    Phone 
WHERE   phone_key IN
        (
        SELECT  address_key
        FROM    person_address
        WHERE   peson_key IN (person_list)
        );

SELECT  *
FROM    Email
WHERE   address_key IN
        (
        SELECT  address_key
        FROM    person_address
        WHERE   peson_key IN (person_list)
        );

You can create a materialized view for this query:

CREATE MATERIALIZED VIEW v_address
REFRESH FORCE ON COMMIT
AS
SELECT  address_key
FROM    person_address
WHERE   person_key IN (person_list)

, or create a temporary table and populate it:

CREATE GLOBAL TEMPORARY TABLE tt_address (VARCHAR2(50));

INSERT
INTO   tt_address
SELECT  address_key
FROM    person_address
WHERE   person_key IN (person_list)

, but, really, if you index your person_key , it's OK to reuse the subquery.

Since you have 3 separate queries, you need your values to be visible to them one way or another.

That means you need to store these values somewhere, be it memory, temporary tablespace or a permanent tablespace.

But the values you need are already stored in the person_address , all you need is to fetch them.

Using the subquery 3 times will involve 12 index scans to fetch the ROWID 's from the index on person_key and 12 table ROWID lookups to fetch address_key from the table. Then most probably a HASH TABLE will be built over them.

This is a matter of microseconds.

Of course, the temporary table or a materialized view would be a little more efficient, but changing the subquery time from 100 microseconds to 50 is hardly worth it, provided that the main queries can take minutes.

Use the with clause. I didn't re-create your exact example issue, but any number of repeated sub-queries can be put in the WITH clause and then referenced in the query.

WITH  address_keys as (
        SELECT  address_key
        FROM    person_address
        WHERE   peson_key IN (person_list)
        )
Select * from table1, table2, address_keys
where table1.address_key = address_keys.address_key
and table2.address_key = address_keys.address_key

First I think that in most cases this optimization does not bring significant improvements(after the first query the data blocks of PERSON_ADDRESS would be mostly cached in the buffer cache and therefore not read from HDD).

However as a case study or for whatever reason: You need to cache the repeating query results and later reuse them in 3 selects. This can be achieved by a (temp) table or MV or a plsql structure varray.

First two options covers Quassnoi in his answer so I won't mention them. The third one has disadvantage in having to state the maximum count of rows in advance (and I don't know right know what happens when you declare a varray with upper bound of 1M or 1G items even if you need only 1k).

--creating db object to hold the data - maximum of 1000 items allowed. 
--I assume that key is number(10,0).
create type t_address_keys is varray (1000) of number (10,0); 

declare 
  la_address_keys t_address_keys; --declare cache variable
begin

--cache keys
SELECT  address_key 
bulk collect into la_address_keys
        FROM    person_address
        WHERE   peson_key IN (person_list);

SELECT  *
into ...
FROM    Address
WHERE   address_key IN table(la_address_keys);

SELECT  *
into ...
FROM    Phone
WHERE   address_key IN table(la_address_keys);

SELECT  *
into ...
FROM    email
WHERE   address_key IN table(la_address_keys);

end;
/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM