In PostgreSQL, I would like to select a row based on some criteria, but if no row matches the criteria, I would like to return the first row. The table actually contains an ordinal column, so the task should be easier (the first row is the one with ordinal 0). For example:
SELECT street, zip, city
FROM address
WHERE street LIKE 'Test%' OR ord = 0
LIMIT 1;
But in this case, there is no way to guarantee the order of the records that match, and I have nothing to order them by. What would be the way to do this using a single SELECT statement?
Shorter (and correct)
You don't actually need a
WHEREclause at all:!~~is just the Postgres operator forNOT LIKE. You can use either. Note that by inverting the logic (NOT LIKEinstead ofLIKE), we can now use defaultASCsort order and NULLs sort last, which may be important. Read on.This is shorter (but not necessarily faster). It is also subtly different (more reliable) than the currently accepted answer by @Gordon.
When sorting by a
booleanexpression you must understand how it works:The currently accepted answer uses
ORDER BY <boolean expression> DESC, which would sort NULLs first. In such a case you should typically addNULLS LAST:If
streetis definedNOT NULLthis is obviously irrelevant, but that has not been defined in the question. (Always provide the table definition.) The currently accepted answer avoids the problem by excluding NULL values in theWHEREclause.Some other RDBMS (MySQL, Oracle, ..) don't have a proper
booleantype like Postgres, so we often see incorrect advice from people coming from those products.Your current query (as well as the currently accepted answer) need the
WHEREclause - or at leastNULLS LAST. With the different expression inORDER BYneither is necessary.More importantly, yet, if multiple rows have a matching
street(which is to be expected), the returned row would be arbitrary and could change between calls - generally an undesirable effect. This query picks the row with the smallestordto break ties and produces a stable result.This form is also more flexible in that it does not rely on the existence of a row with
ord = 0. Instead, the row with the smallestordis picked either way.Faster with index
(And still correct.) For big tables, the following index would radically improve performance of this query:
Detailed explanation:
Depending on undefined details it may pay to add more columns to the index.
The fastest query using this index:
BTW, this is a single statement.
This is more verbose, but allows for a simpler query plan. The second
SELECTof theUNION ALLis never executed if the firstSELECTproduces enough rows (in our case: 1). If you test withEXPLAIN ANALYZE, you'll see(never executed)in the query plan.Details:
Evaluation of
UNION ALLIn reply to Gordon's comment. Per documentation:
Bold emphasis mine.
And
LIMITmakes Postgres stop evaluating as soon as enough rows are found. That's why you see(never executed)in the output ofEXPLAIN ANALYZE.If you add an outer
ORDER BYbefore the finalLIMITthis optimization is not possible. Then all rows have to be collected to see which might sort first.