I'm writing a function in node.js to query a PostgreSQL table.
If the row exists, I want to return the id column from the row.
If it doesn't exist, I want to insert it and return the id (insert into ... returning id
).
I've been trying variations of case
and if else
statements and can't seem to get it to work.
A solution in a single SQL statement. Requires PostgreSQL 8.4 or later though.
Consider the following demo:
Test setup:
INSERT / SELECT command:
The first CTE v is not strictly necessary, but achieves that you have to enter your values only once.
The second CTE s selects the
id
fromtbl
if the "row" exists.The third CTE i inserts the "row" into
tbl
if (and only if) it does not exist, returningid
.The final
SELECT
returns theid
. I added a columnsrc
indicating the "source" - whether the "row" pre-existed andid
comes from a SELECT, or the "row" was new and so is theid
.This version should be as fast as possible as it does not need an additional SELECT from
tbl
and uses the CTEs instead.To make this safe against possible race conditions in a multi-user environment:
Also for updated techniques using the new UPSERT in Postgres 9.5 or later:
I would suggest doing the checking on the database side and just returning the id to nodejs.
Example:
And than on the Node.js-side (i'm using node-postgres in this example):
Something like this, if you are on PostgreSQL 9.1
It's a bit longish and you need to repeat the id to test for several times, but I can't think of a different solution that involves a single SQL statement.
If a row with
id=42
exists, the writeable CTE will not insert anything and thus the existing row will be returned by the second union part.When testing this I actually thought the new row would be returned twice (therefor a
union
not aunion all
) but it turns out that the result of the second select statement is actually evaluated before the whole statement is run and it does not see the newly inserted row. So in case a new row is inserted, it will be taken from the "returning" part.