tags:

views:

220

answers:

3

I have a php script to insert new parts into a postgres database. I want to check if the part exists before it is created. How can I do this. I am using the usual INSERT INTO clause.

+2  A: 

You SELECT first, to know if you must INSERT or UPDATE, like:

if (
    $db->fetchOne(
        'SELECT COUNT(1) FROM parts WHERE article_number = ?',
        $p->article_number
    )
) {
    $db->update(
        'parts',
        $p->to_array(),
        $db->quoteInto('article_number = ?', $p->article_number)
    );
}
else {
    $db->insert('parts', $p->to_array());
}

Re Milen's comment: great point! With the default PostgreSQL transaction isolation level (READ COMMITTED) it's possible that another process inserts (and commits) "your" part after your SELECT, but before your INSERT.

For the kind of apps we build, we normally just catch the DB exception, and do something about it (inform the user, retry). Or you can go with SET TRANSACTION ISOLATION LEVEL SERIALIZABLE. See Transaction Isolation.

Ivan Krechetov
Ok, do a SELECT else clause. I am a newbie at this, may you assist by showing me the code? I can do the SELECT but not the else bit. Many thanks for your great mind.
russell kinch
I've added the code Russel. $db is a Zend_Db_Adapter http://framework.zend.com/manual/en/zend.db.html#zend.db.adapter
Ivan Krechetov
What happens when a record with the same article_number was inserted right after the check for existence and before the inserting (a.k.a. race condition)?
Milen A. Radev
Thanks for your comment Milen. It's definitely something to be aware of. I've updated my answer.
Ivan Krechetov
+1  A: 

If you rarely expect conflicts: Take the optimistic approach and just insert the new one and handle the uniqueness violation if it occurs. This will invalidate the current transaction when it fails, so if you're doing a lot of work in it, it'll be worthwhile to check first. In that case, I prefer SELECT EXISTS(SELECT 1 ... FROM ... WHERE ...).

Anyway, you must handle the possibility for a uniqueness violation. Even if you check first, there is a possibility that a concurrent transaction inserts the same record and commits first.

Alex Brasetvik
+2  A: 
insert into parts(article_number,category) 
select 'KEYB','COMPTR' 
where not exists(select * from parts where article_number = 'KEYB')
Michael Buen
great solution, if we want to ignore duplicates silently. does not suffer race condition. I use it myself.
filiprem