views:

4448

answers:

5

I have Couple of scenarios 1) need to read the value of a column from three different tables in a predefined order and only 1 tabel will have the data 2) read data from table1 if records are present for criteria given else Read Data from Table2 for Given Criteria

In Oracle Stored Procedures The way these are being handled right now is to first get the count for a given query into a variable and if the count > 0, then we execut the same query to read the actual data as in

select count(*) from table1 into v_count
if v_count > 0
then
    select data into v_data from table1
end if;

Return v_data

This is being done to avoid the no_data_Found exception, other wise i would need three exception handler blocks to catch the no_data_found exception for each table access

Currently i am reimplementing this with Cursors so that i have something like this

cursor C1 is
    select data from table1;
Open C1
Fetch C1 into v_data
if C1%FOUND
then
    Close C1
    Return v_data
End If

I wanted to find out which one is better from a performance point of view the one with Cursors or the one which does a Select into a variable and has three no_data_found Exception blocks. I dont want to use the Current Two Stage query process which we have currently

+1  A: 
select count(*) from table1 into v_count
if v_count > 0 then
    select data into v_data from table1;
else
    v_data := null;
end if;
return v_data;

is NOT equivalent to

begin
    select data into v_data from table1;
    return v_data;
exception
    when no_data_found then
        return null;
end;

in a multi-user environment. In the first case, someone could update the table between the points where you check for existence and when you read the data.

Performance-wise, I have no idea which is better, but I know that the first option makes two context switches to the sql engine and the second one only does one context switch.

erikkallen
since i am going to use this in a reporting environment, multi user access does not make much difference. If it was just one table access then the exception mechanism is ok,but just thinking about using exceptions when i have close to 3 tables to access in the similar way
Dinesh Manne
I would do it anyway. Doesn't cost many source lines and your intentions are clear.
erikkallen
A: 

Use the "for row in cursor" form of a loop and the loop will just not process if there is no data:

declare cursor
t1Cur is
 select ... from table1;
t2Cur is
 select ... from table2;
t3Cur is
 select ... from table3;
t1Flag boolean FALSE;
t2Flag boolean FALSE;
t3Flag boolean FALSE;
begin
for t1Row in t1Cur loop
  ... processing, set t1Flag = TRUE
end loop;
for t2Row in t2Cur loop
  ... processing, set t2Flag = TRUE
end loop;
for t3Row in t3Cur loop
  ... processing, set t3Flag = TRUE
end loop;
... conditional processing based on flags
end;
dpbradley
I dont see any difference between the option you are suggesting and the cursor option which i mention, in terms of performance, i am doing an explicit check for data found, the real performance issue if will be in the cursor, open, fetch close.
Dinesh Manne
My code didn't illustrate what I meant - got tied up in reformatting and lost the main idea. If only one of the tables has the data, and the order is predefined, then you just add a check for the preceding boolean flag prior to executing the next cursor loop. You will only execute as many selects as needed, and don't have to handle the no data execeptions.
dpbradley
A: 

The way you're handling scenario 1 now is not good. Not only are you doing two queries when one will suffice, but as Erik pointed out, it opens up the possibility of data changing between the two queries (unless you use a read-only or serializable transaction).

Given that you say that in this case the data will be in exactly one of three tables, how about this?

SELECT data
  INTO v_data FROM
  (SELECT data FROM table1
   UNION ALL
   SELECT data FROM table2
   UNION ALL
   SELECT data FROM table3
  )

Another "trick" you can use to avoid writing multiple no-data-found handlers would be:

SELECT MIN(data) INTO v_data FROM table1;
IF v_data IS NOT NULL THEN
   return v_data;
END IF;

SELECT MIN(data) INTO v_data FROM table2;
...etc...

but I don't really see any reason that's better than having three exception handlers.

For your second scenario, I think what you mean is that there may be data in both tables and you want to use the data from table1 if present, otherwise use the data from table 2. Again you could do this in a single query:

SELECT data
  INTO v_data FROM
  (SELECT data FROM
    (SELECT 1 sort_key, data FROM table1
     UNION ALL
     SELECT 2 sort_key, data FROM table2
    )
   ORDER BY sort_key ASC
  )
  WHERE ROWNUM = 1
Dave Costa
I did think of Union All,but stayed away from it, because currently that will peform much worse then First sceanrio which i described, as one of the tables is accessed through a db link, for both the cases i wanted to avoid additional hits to the the other tables when data does not exist, with the union all, my performance worsens
Dinesh Manne
The MIN option looks resonable, though i am not sure whether i will end up using it
Dinesh Manne
+2  A: 

I don't know why you are so keen to avoid the exception? What is wrong with:

begin
    begin
        select data into v_data from table1;
    exception
        when no_data_found then
        begin
            select data into v_data from table2;
        exception
            when no_data_found then
            begin
               select data into v_data from table3;
            exception
                when no_data_found then
                    v_data := null;
            end;
        end;
    end;
    return v_data;
end;

I believe this will perform better than your other solution because it does the minimum possible work to achieve the desired result.

See How bad is ignoring Oracle DUP_VAL_ON_INDEX exception? where I demonstrate that using exceptions performs better than counting to see if there is any data.

Tony Andrews
I thought Exceptions have an overhead
Dinesh Manne
No - see http://stackoverflow.com/questions/350860/how-bad-is-ignoring-oracle-dupvalonindex-exception where I demonstrate that using exceptions performs better than counting to see if there is any data.
Tony Andrews
Of course when I say "no", I mean no *significant* overhead that means they need to be avoided!
Tony Andrews
I decided the best way was to just code both approaches and get some metrics, which were interesting, One was the cusrsor approach and the other exception approach. I decided to loop 1000 times for the same set, The Exception approach performed very poor with each iteration at 1 minute, as against the Cursor Approach which was 9.5 Sec overall. This was becasue the 3rd statement was accessed on a database link. Once i Removed that Dblink query, the exception approach is faster by 0.02 seconds.
Dinesh Manne
A: 
DECLARE

A VARCHAR(35); B VARCHAR(35); BEGIN WITH t AS (SELECT OM_MARCA, MAGAZIA FROM ifsapp.AKER_EFECTE_STOC WHERE (BARCODE = 1000000491009)) SELECT (SELECT OM_MARCA FROM t) OM_MARCA, (SELECT MAGAZIA FROM t) MAGAZIA INTO A, B FROM DUAL; IF A IS NULL THEN dbms_output.put_line('A este null'); END IF; dbms_output.put_line(A); dbms_output.put_line(B); END; /

ValiTl