tags:

views:

129

answers:

4

What is the more easy way to INSERT a row if it doesn't exist, in PL/SQL (oracle)?

I want something like:

IF NOT EXISTS (SELECT * FROM table WHERE name = 'jonny') THEN
  INSERT INTO table VALUES ("jonny", null);
END IF;

But it's not working.

Note: this table has 2 fields, say, name and age. But only name is PK.

+8  A: 
INSERT INTO table
SELECT 'jonny', null
  FROM dual 
 where not exists (select 1
                     from table
                    where name = 'jonny'
                  )
Benoit
What is "dual" in this code? I must be missing something.
Jeff Walker
@Jeff Walker: [See this question](http://stackoverflow.com/questions/3732422/select-from-nothing/3732466#3732466)
OMG Ponies
dual is a dummy table in Oracle with one column and one row. It is bad (in SQLite you can just Select without from, in Oracle you have to use dual when selecting from nowhere).
Benoit
+1 and accept...
Topera
-1 This will not work when more than one session tries to insert the same row at the same time - neither will see the other session's data until they commit, at which time it is too late. The best solution is to apply a unique constraint.
Jeffrey Kemp
+5  A: 

If name is a PK, then just insert and catch the error. The reason to do this rather than any check is that it will work even with multiple clients inserting at the same time. If you check and then insert, you have to hold a lock during that time, or expect the error anyway.

The code for this would be something like

BEGIN
  INSERT INTO table( name, age )
    VALUES( 'johnny', null );
EXCEPTION
  WHEN dup_val_on_index
  THEN
    NULL; -- Intentionally ignore duplicates
END;
Lou Franco
Code:`BEGIN INSERT INTO table VALUES('jonny', null);EXCEPTION WHEN sqlcode != -1 THEN RAISE;END; /`sqlcode = -1 when ORA-00001
Benoit
Whether it makes sense to try the insert and catch the exception depends on how frequently you expect the INSERT to succeed. If 99% of the time you're inserting a non-duplicate value and it will only error out 1% of the time, catching and ignoring the exception is a good option. If 99% of the time the row already exists, catching the exception can be problematic from a performance perspective.
Justin Cave
+3  A: 

Assuming you are on 10g, you can also use the MERGE statement. This allows you to insert the row if it doesn't exist and ignore the row if it does exist. People tend to think of MERGE when they want to do an "upsert" (INSERT if the row doesn't exist and UPDATE if the row does exist) but the UPDATE part is optional now so it can also be used here.

SQL> create table foo (
  2    name varchar2(10) primary key,
  3    age  number
  4  );

Table created.

SQL> ed
Wrote file afiedt.buf

  1  merge into foo a
  2    using (select 'johnny' name, null age from dual) b
  3       on (a.name = b.name)
  4   when not matched then
  5    insert( name, age)
  6*    values( b.name, b.age)
SQL> /

1 row merged.

SQL> /

0 rows merged.

SQL> select * from foo;

NAME              AGE
---------- ----------
johnny
Justin Cave
A: 

Using parts of @benoit answer, I will use this:

DECLARE
    varTmp NUMBER:=0;
BEGIN
    -- checks
    SELECT nvl((SELECT 1 FROM table WHERE name = 'john'), 0) INTO varTmp FROM dual;

    -- insert
    IF (varTmp = 1) THEN
        INSERT INTO table (john, null)
    END IF;

END;

Sorry for I don't use any full given answer, but I need IF check because my code is much more complex than this example table with name and age fields. I need a very clear code. Well thanks, I learned a lot! I'll accept @benoit answer.

Topera