Relational Database Systems Concepts, Design, SQL, PostgreSQL, MySQL, and Applications

Appendices

Appendix I. PostgreSQL versus MySQL Syntax Reference

Chapter 17's ledger, expanded to the full working map: every platform difference this book met, with the PostgreSQL and MySQL spellings side by side and the chapter where it was taught.

Statements and clauses

NeedPostgreSQLMySQL 8.0
Concatenation'a' || 'b'CONCAT('a', 'b')
Case-insensitive LIKEILIKE 'x'LIKE 'x' (default collation) or LOWER(col) LIKE ...
Regex matchcol ~ 'pat' / ~*REGEXP_LIKE(col, 'pat') / col REGEXP 'pat'
LIMIT / offsetLIMIT n OFFSET mLIMIT m, n or LIMIT n OFFSET m
Standard fetchOFFSET m ROWS FETCH NEXT n ROWS ONLY(not supported)
NULL placement in ORDER BYNULLS FIRST / LASTnone — sort on col IS NULL, col
UpsertON CONFLICT (k) DO UPDATE SET x = EXCLUDED.xON DUPLICATE KEY UPDATE x = VALUES(x)
DML result rowsRETURNING colROW_COUNT(), LAST_INSERT_ID()
INTERSECT / EXCEPTnative8.0.31+ (joins before)
FULL OUTER JOINnativeemulate: LEFT JOIN ... UNION ALL ... RIGHT JOIN ... WHERE IS NULL
Quoting identifiers"name"backquotes (ANSI_QUOTES mode: double quotes)
Comments in DDLCOMMENT ON TABLE/COLUMNCOMMENT '...' in-line

Types and identifiers

ConceptPostgreSQLMySQL 8.0
Exact decimalNUMERIC(p,s)DECIMAL(p,s) (synonyms both)
Unlimited stringtext (preferred)TEXT tiers
BooleanbooleanTINYINT(1) convention
Zone-aware timestamptimestamptzTIMESTAMP (UTC-converted, 1970–2038)
Literal timestamptimestampDATETIME (1000–9999)
UUIDuuidBINARY(16) / UUID() strings
Auto keyGENERATED ... AS IDENTITY / SERIALAUTO_INCREMENT
Enumerated domainENUM type + CHECK (preferred)ENUM(...) column type
Arrays / ranges / domainsnativenone (JSON approximates)

Functions

TaskPostgreSQLMySQL
CharactersLENGTH(s)CHAR_LENGTH(s)
BytesOCTET_LENGTH(s)LENGTH(s)
FindPOSITION(sub IN s)LOCATE(sub, s)
Split fieldsplit_part(s, ',', n)SUBSTRING_INDEX(s, ',', n)
Date addd + INTERVAL '1 year'DATE_ADD(d, INTERVAL 1 YEAR)
Date diffAGE(a, b) (interval)DATEDIFF(a, b) days; TIMESTAMPDIFF(unit, a, b)
Date truncatedate_trunc('month', ts)DATE_FORMAT(ts, '%Y-%m-01') + cast
Formatto_char(d, 'YYYY-MM')DATE_FORMAT(d, '%Y-%m')
Parseto_date(s, 'YYYY-MM-DD')STR_TO_DATE(s, '%Y-%m-%d')
Conditional valueCASE (also IF in procedures)CASE and IF(cond, a, b)
NULL substituteCOALESCE(a, b)COALESCE(a, b) / IFNULL(a, b)
Grouped stringstring_agg(x, ', ' ORDER BY y)GROUP_CONCAT(x ORDER BY y SEPARATOR ', ') (mind max_len)
Grouped arrayarray_agg(x)none
Conditional countCOUNT(*) FILTER (WHERE c)SUM(CASE WHEN c THEN 1 ELSE 0 END)
Per-group first rowDISTINCT ON (k) ... ORDER BY k, xROW_NUMBER() OVER (PARTITION BY k ...) + filter
Series of rowsgenerate_series(1, n)recursive CTE / numbers table
CastCAST(x AS t) / x::tCAST(x AS t) / CONVERT(x, t)

JSON

TaskPostgreSQLMySQL 8.0
Typesjson, jsonb (use jsonb)JSON
Extract (typed / text)-> 'k' / ->> 'k'-> '$.k' / ->> '$.k'
Longhand extractjsonb_extract_pathJSON_EXTRACT
Containment@>, ?, ?&, ?|JSON_CONTAINS, JSON_OVERLAPS
Update in placejsonb_set, ||JSON_SET, JSON_MERGE_PATCH
Rows from JSONjsonb_array_elementsJSON_TABLE
JSON from rowsjsonb_agg, jsonb_build_objectJSON_ARRAYAGG, JSON_OBJECTAGG
IndexingGIN on the column (containment)multi-valued index (8.0.17) on arrays

Constraints, DDL, and transactions

AspectPostgreSQLMySQL 8.0
CHECK enforcementalways8.0.16+
Deferred checkingDEFERRABLE INITIALLY DEFERREDnone (immediate)
Add FK onlineNOT VALID + VALIDATE CONSTRAINTALGORITHM=INPLACE, LOCK=NONE online DDL
Change column typeALTER COLUMN ... TYPE tMODIFY COLUMN t
Drop constraintDROP CONSTRAINT nameDROP INDEX name / DROP FOREIGN KEY name
Table DDL review\d tableSHOW CREATE TABLE table
Storage enginesone engineper-table ENGINE= (InnoDB default)
DDL in transactionstransactional (rollback-able)implicit COMMIT
Default isolationREAD COMMITTEDREPEATABLE READ (phantoms blocked by next-key locks)
Set isolationSET SESSION CHARACTERISTICS ...SET SESSION TRANSACTION ...
Deadlock errorSQLSTATE 40P01SQLSTATE 40001 (ERROR 1213)
Materialized viewsnative + REFRESH [CONCURRENTLY]emulate: table + event/trigger
Scheduled SQLcron / pg_cron (external)CREATE EVENT (built-in scheduler)
Index extraspartial, expression, INCLUDE, GIN/GiST/BRIN/hashprefix, functional key parts, FULLTEXT, spatial
Bulk loadCOPY (server) / \copy (client)LOAD DATA [LOCAL] INFILE, INTO OUTFILE
File sandboxserver OS user / path visibilitysecure_file_priv / local_infile

The portability method (Chapter 17 recap)

  1. Write the standard spelling where one exists.
  2. Catalog every divergence here in the project's dialect document.
  3. Test on both platforms during development — the parity proof (Chapter 28 Lab 10).
  4. Confine dialect to one layer (the repository / query module).
  5. Treat differences in rounding and ordering as expected (AVG precision, NULL placement) and differences in rows as bugs.