views:

47

answers:

2

EDIT: changed the title to fit the code below.

I'm trying to retrieve a list of acceptable values from an Oracle table, then performing a SELECT against another while comparing some fields against said list.

I was trying to do this with cursors (like below), but this fails.

DECLARE
    TYPE gcur IS REF CURSOR;
    TYPE list_record IS TABLE OF my_table.my_field%TYPE;
    c_GENERIC gcur;
    c_LIST list_record;
BEGIN
    OPEN c_GENERIC FOR
    SELECT my_field FROM my_table
    WHERE some_field = some_value;

    FETCH c_GENERIC BULK COLLECT INTO c_LIST;

    -- try to check against list
    SELECT * FROM some_other_table
    WHERE some_critical_field IN c_LIST;

END

Basically, what I'm trying to do is to cache the acceptable values list into a variable, because I will be checking against it repeatedly later.

How do you perform this in Oracle?

+1  A: 

Why pull the list instead of using a semi-join?

SELECT * 
  FROM some_other_table 
 WHERE some_critical_field IN (SELECT my_field 
                                FROM my_table
                               WHERE some_field = some_value); 
Adam Musch
Because the primary SELECT statement may be performed repeatedly. I was thinking that any nested SELECTs would be inefficient if the number of iterations was really high, so I'm trying to cache my list instead.
Richard Neil Ilagan
Not to sound snide, but it sounds like you think you have a performance optimization problem, not that you know you have one. I'd recommend benchmarking what I've provided vs. APC's solution to see if there's any significant difference.
Adam Musch
Actually, I do know I have an optimization problem. I'm stuck refactoring someone else's code well into the dawn right now because an SP made up of blocks like the code I pseudo-typed above is buckling against our large data sets. :) Truth be told, I'm really interested in benchmarking both your solutions to sate my curiosity, but it's just that I'd rather try something new to get this finally over with. :D
Richard Neil Ilagan
Provided the nested sub-query doesn't reference anything in the main query, Oracle should determine that the sub-query can be executed once, and then re-use the result set. That should be identical to having C_LIST as a variable / object collection type. We all know the optimiser can often decide to do the worst possible thing instead. Hints may help, or otherwise forcing a different execution plan.
JulesLt
+3  A: 

We can use collections to store values to suit your purposes, but they need to be declared as SQL types:

create type list_record is table of varchar2(128)
/

This is because we cannot use PL/SQL types in SQL statements. Alas this means we cannot use %TYPE or %ROWTYPE, because they are PL/SQL keywords.

Your procedure would then look like this:

DECLARE
    c_LIST list_record;
BEGIN

    SELECT my_field 
    BULK COLLECT INTO c_LIST 
    FROM my_table
    WHERE some_field = some_value;

    -- try to check against list
    SELECT * FROM some_other_table
    WHERE some_critical_field IN ( select * from table (c_LIST);

END;    

"I see that you still had to perform a SELECT statement to populate the list for the IN clause."

If the values are in a table there is no other way to get them into a variable :)

"I'm thinking that there's a significant performance gain using this over a direct semi-join"

Not necessarily. If you're only using the values once then the sub-query is certainly the better approach. But as you want to use the same values in a number of discrete queries then populating a collection is the more efficient approach.

In 11g Enterprise Edition we have the option to use result set caching. This is a much better solution, but one which is not suited for all tables.

APC
Thanks. I get what you're saying, but I see that you still had to perform a SELECT statement to populate the list for the IN clause. I'm thinking that there's a significant performance gain using this over a direct semi-join (as offered by Adam Musch below)? :)
Richard Neil Ilagan