tags:

views:

1112

answers:

4

I'm using Oracle, and I have a very large table. I need to check for the existence of any row meeting some simple criteria. What's the best way to go about this using simple SQL?

Here's my best guess, and while it may turn out to be fast enough for my purposes, I'd love to learn a canonical way to basically do SQL Server's "exists" in Oracle:

select count(x_id) from x where x.col_a = value_a and x.col_b = value_b;

The count() would then be returned as a boolean in another tier. The main point is that I want Oracle to do the bare minimum for this query - I only need to know if there are any rows matching the criteria.

And yes, those columns will most definitely be indexed.

+2  A: 
SELECT  NULL
FROM    x
WHERE   x.col_a = value_a
        AND x.col_b = value_b
        AND rownum = 1

COUNT(*) is certainly not the best way since it will need to count all the rows, while ROWNUM = 1 returns as soon as it finds the first matching row.

Here's the PL/SQL code:

DECLARE
        ex INT;
BEGIN
        BEGIN
                SELECT  NULL
                INTO    ex
                FROM    dual
                WHERE   1 = 1
                        AND rownum = 1;
                DBMS_OUTPUT.put_line('found');
        EXCEPTION
        WHEN no_data_found THEN
                DBMS_OUTPUT.put_line('not found');
        END;
END;
Quassnoi
Maybe even an FIRST_ROWS hint? Optimizer should do this implicitly when it sees the rownum=1 I think.SELECT /*+ FIRST_ROWS(n) */ NULL
Robert Merkwürdigeliebe
@Robert: of course it won't hurt to add one, but it really works only in joins (it makes the optimizer to prefer NESTED LOOP's over HASH JOIN's)
Quassnoi
@Quassnoi : I thought it makes the optimizer 'index happy' too.
Robert Merkwürdigeliebe
@Robert: indexes are good for NESTED LOOP's but not always good for HASH JOIN's. In this sense, yes, FIRST_ROWS makes it use NESTED LOOPS which in turn use indexes. But for a single table query the index will be used anyway: there is no benefit from using a fullscan here and the optimizer most probably will not even consider it unless explicitly told so.
Quassnoi
The ROWNUM=1 is sufficient to cause the optimiser to pick a plan that will find the first row ASAP.
Jeffrey Kemp
Why not just use EXISTS? Oracle will do the rownum=1 optimisation for you.
Nick Pierpoint
You need a SELECT query. You can't just use IF EXISTS like in SQL Server
Quassnoi
+7  A: 

Using COUNT(*) is OK if you also use rownum=1:

declare
   l_cnt integer;
begin
   select count(*)
   into   l_cnt
   from   x
   where  x.col_a = value_a 
   and    x.col_b = value_b
   and    rownum = 1;
end;

This will always return a row, so no need to handle any NO_DATA_FOUND exception. The value of l_cnt will be 0 (no rows) or 1 (at least 1 row exists).

Tony Andrews
@Tony - would you do a count(*) instead of an EXISTS? To me, an EXISTS seems more natural even if you do need to deal with the exception.
Nick Pierpoint
Do you mean "select 1 from dual where exists (...)"? I wouldn't, but can understand why some would. What is really wanted to be trully natural is a PL/SQL construct like "IF exists (select ...) THEN ..."!
Tony Andrews
I'm being lazy here by not testing it myself, but wouldn't count(x_id) be quicker than count(*), or is the sql interpreter smart enough to see that it doesn't really need to expand "*"?
Josh Kodroff
count(*) is optimised to be as efficient as any other method.
Tony Andrews
A: 
begin
select 'row DOES exist' 
  into ls_result
from dual
where exists (select null from x where x.col_a = value_a and x.col_b = value_b);
exception
when no_data_found then
  ls_result := ' row does NOT exist';
end;
Michal Pravda
+1  A: 

I think using EXISTS gives a more natural answer to the question than trying to optimise a COUNT query using ROWNUM.

Let Oracle do the ROWNUM optimisation for you.

create or replace function is_exists (
        p_value_a varchar2,
        p_value_b varchar2)
        return boolean
is

   v_exists varchar2(1 char);

begin

    begin
        select 'Y' into v_exists from dual
        where exists
            (select 1 from x where x.col_a = p_value_a and x.col_b = p_value_a);

    exception

        when no_data_found then

            v_exists := null;

    end;

    return v_exists is not null;

end is_exists;
Nick Pierpoint