1

I would like to write a function/procedure in Postgres with 2 SELECT statements. First I select from table1 and if there is no record matching in table1 then perform the same SELECT on table2. E.g.

function getRecord(String: inValue){
  If (select col1, col2 from table1 where col3=inValue) then
    return the row (with col1 and col2);
  else
    return select col1, col2 from TABLE2 where col3=inValue;
}

Is it possible to have a function like this in PostgreSQL (ver 9.1 and above)?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
mosheh
  • 41
  • 5

2 Answers2

1

You probably don't need a function, just the query. But here goes:

PL/pgSQL function

CREATE OR REPLACE FUNCTION get_record(_value text)
  RETURNS TABLE(col1 int, col2 text) AS  -- replace with actual columns / types
$func$
BEGIN

RETURN QUERY
SELECT col1, col2 FROM table1 WHERE col3 = _value;

IF NOT FOUND THEN
   SELECT col1, col2 FROM table2 WHERE col3 = _value;
END IF;

END
$func$ LANGUAGE plpgsql;
  • You can use the special variable FOUND to check.

  • This can return any number of rows, the second query is called if the first does not return anything.

SQL function

CREATE OR REPLACE FUNCTION get_record(_value text)
  RETURNS TABLE(col1 int, col2 text) AS  -- replace with actual columns / types
$func$

SELECT col1, col2 FROM table1 WHERE col3 = _value
UNION ALL
SELECT col1, col2 FROM table2 WHERE col3 = _value
LIMIT  1;

$func$ LANGUAGE sql;
  • If the parameter is on a unique column and a single row is guaranteed.

  • In older versions you need to use positional references for parameters in SQL functions. i.e.: $1 instead of _value in the body. (You can still name the parameter in the function header, which is useful for documentation and calling with named parameters.)

  • Without parentheses the LIMIT 1 applies to the whole of the query, not just the last leg. Without ORDER BY! in the outer query Postgres calls each SELECT until enough rows are returned to satisfy the LIMIT clause. If you run ANALYZE for the query you will see "never executed" for remaining legs of the UNION query.

Related answers:

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • Erwin thanks so much. I think this is what I need. Also the select query must return only on record either found in table1 or in table2 so I have LIMIT=1. THANKS A MILLION for the tip. – mosheh Jul 20 '14 at 12:17
0

Might be able to do it with a query like this:

SELECT COALESCE(a.col1, b.col1) AS col1
      ,COALESCE(a.col2, b.col2) AS col2
  FROM table1 a
  LEFT OUTER JOIN table2 AS b ON (a.col3 = inValue AND b.col3 = a.col3)

This assumes col1 and col2 are not nullable. Might also be able to approach it with something like this:

SELECT col1, col2
  FROM ( SELECT col1, col2, 1 AS ordering
           FROM table1
           WHERE col3 = inValue
         UNION
         SELECT col1, col2, 2 AS ordering
           FROM table2
           WHERE col3 = inValue
       ) x
   ORDER BY ordering
   LIMIT 1;

If you really want to do it in a stored procedure, you can SELECT ... INTO local variables from table1 and then check IF NOT FOUND THEN to see if you need to query the other table. Then return the row created from the local variables. See example mid-way down this page http://www.postgresql.org/docs/9.1/static/plpgsql-statements.html.

Glenn
  • 8,932
  • 2
  • 41
  • 54
  • Great help Glenn. Thanks. I will try both Erwin's and your suggestions and see which one is faster (Note: table1 and table2 are rather large and I need to optimize these selects - I had put this code in my Java application as separate selects statements but they seem to take a none trivial time to execute - this is why I am looking for a solution to put them in a postgres function/procedure so that I only make one jdbc call to the db instead of 2 as in the current code) - I will test and see if I have some speed improvement. Tx a lot. – mosheh Jul 20 '14 at 12:27