SQLMentor // articles

PostgreSQL vs Oracle: Key SQL Differences to Know

PostgreSQL and Oracle share the same relational core, but licensing, procedural code, identifier case-folding, and even how NULL behaves in string concatenation all differ enough to catch people off guard when moving between them. Here's what actually changes.

Same relational core, different everywhere else

Both PostgreSQL and Oracle Database are mature, standards-compliant relational databases — the SELECT/JOIN/GROUP BY core you already know transfers almost unchanged. The differences show up in licensing, procedural code, data types, and a handful of behavioural quirks that catch people migrating between the two off guard.

Quick comparison

PostgreSQLOracle
Licence & costfree, open source (PostgreSQL Licence)commercial; free Express Edition (XE) available with limits
Procedural languagePL/pgSQLPL/SQL
Default identifier caselowercase (unquoted identifiers folded down)UPPERCASE (unquoted identifiers folded up)
Concatenating with NULL (x || 'text')returns NULL if x is NULLreturns 'text' — Oracle treats NULL as an empty string in concatenation
Row-limiting clauseLIMIT / OFFSET (always available)FETCH FIRST / OFFSET (12c+) or the older ROWNUM pattern
Native JSON supportjson and jsonb types, rich operatorsJSON type and functions since 12c, less native than jsonb
Autoincrement columnsSERIAL / IDENTITY (backed by a sequence)sequence + trigger, or IDENTITY columns (12c+)
MVCC old-version storageold row versions kept in the table heap; VACUUM reclaims spaceold versions kept in separate undo segments
Physical row identifierctid (not stable across VACUUM FULL)ROWID (a Oracle-managed pseudocolumn)

The identifier case-folding trap

This one causes more "table not found" confusion than anything else on this list. Both databases fold unquoted identifiers to a default case — but in opposite directions: PostgreSQL folds to lowercase, Oracle folds to UPPERCASE.

So CREATE TABLE Employees (...) silently becomes a table named employees in PostgreSQL, but EMPLOYEES in Oracle. If you later reference "Employees" with quotes and mixed case, neither database will find it — because a quoted identifier is taken literally, and neither actual table name matches that exact casing.

The NULL-concatenation difference, demonstrated

The same expression, different results — a genuinely important behavioural gap when porting code between the two.

-- Oracle: NULL is treated as an empty string in concatenation
SELECT 'Hello ' || NULL || 'World' FROM dual;   -- 'Hello World'

-- PostgreSQL: NULL poisons the whole expression, standard SQL behaviour
SELECT 'Hello ' || NULL || 'World';             -- NULL

PL/SQL vs PL/pgSQL

Both languages share the same DNA — block structure (DECLARE / BEGIN / EXCEPTION / END), similar cursor and exception-handling concepts — because PL/pgSQL was explicitly designed to feel familiar to PL/SQL developers. But the syntax isn't interchangeable: procedure/function declaration syntax, built-in package names (Oracle's DBMS_OUTPUT vs Postgres's RAISE NOTICE), and many built-in functions differ enough that code needs a real rewrite, not a find-and-replace, when porting between them.

Pagination: always simple in Postgres, version-dependent in Oracle

PostgreSQL has had LIMIT / OFFSET since its earliest versions — no special cases. Oracle only gained the equivalent standard-SQL FETCH FIRST n ROWS ONLY clause in 12c (2013); older Oracle code still relies on the less intuitive ROWNUM pseudocolumn, which has its own gotcha — it's assigned before ORDER BY runs unless you wrap the query in a subquery.

Frequently asked questions

Should I learn Oracle or PostgreSQL first?
Base your choice on what you'll actually use professionally — the core SQL skills transfer either way. PostgreSQL is a reasonable default for a first database if you're not targeting a specific job, since it's free, widely used, and standards-compliant. If your target employer or certification path runs Oracle, learn Oracle SQL directly.
Is PL/pgSQL code compatible with PL/SQL, or vice versa?
No — they're similar in structure but not source-compatible. Migrating procedural code between Oracle and PostgreSQL requires rewriting it, not just changing a connection string. Tools exist to automate parts of this for large migrations, but manual review is still needed.
Why does my PostgreSQL query say a table doesn't exist, even though I just created it?
Check whether you created the table with quoted mixed-case identifiers (like "Employees") and are now querying it unquoted (employees, which PostgreSQL folds to lowercase). Quoted and unquoted identifiers are different names to PostgreSQL — match the casing exactly, or avoid quoted mixed-case names entirely.
Is Oracle always faster than PostgreSQL, or vice versa?
Neither is universally faster — performance depends heavily on workload, indexing, configuration, and hardware. Oracle's licensing includes enterprise-grade tuning and support options that matter for large, complex deployments; PostgreSQL has closed much of the historical feature gap and is extremely competitive for most workloads. Benchmark your actual use case rather than assuming.