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.