Chapter 06 · Functions, NULLs, and Conditional Logic

Vendor Portability and Function Equivalents

Separate standard SQL concepts from dialect-specific spellings so queries can be maintained across database products.

Beginner95–120 minutesDialect comparison + rewrite labLast reviewed: August 2026

Learning outcomes

SQL standards define many concepts, but production engines expose different function names, operators, argument orders, type rules, and date/time models. Portability begins by identifying which layer owns those differences.

01

Compare common string, date/time, NULL, and conversion idioms across major SQL engines.

02

Separate portable expressions from dialect-specific implementations.

03

Choose a strategy for multi-database applications and analytical pipelines.

04

Test return types and edge cases rather than assuming equivalent names mean equivalent behavior.

Portability has several dimensions

N

Name

The same operation may have a different function name.

S

Syntax

Argument order, units, and operators may differ.

T

Type

Return type, precision, collation, and NULL resolution can differ.

B

Behavior

Boundary rules, time zones, rounding, and invalid-input handling can differ.

Define semantic requirement
Choose standard expression when practical
Map each target dialect
Test edge cases and return types

Portability is a tested mapping from meaning to engine behavior, not a search-and-replace exercise.

String-function equivalents

IntentSQLitePostgreSQLMySQLSQL ServerOracle
Length in charactersLENGTH(x)CHAR_LENGTH(x) or LENGTH(x)CHAR_LENGTH(x)LEN(x)LENGTH(x)
SubstringSUBSTR(x,s,n)SUBSTRING(x FROM s FOR n)SUBSTRING(x,s,n)SUBSTRING(x,s,n)SUBSTR(x,s,n)
Concatenatea || ba || b or CONCATCONCAT(a,b)CONCAT(a,b) or a + ba || b or CONCAT
TrimTRIM(x)TRIM(x)TRIM(x)TRIM(x)TRIM(x)
Case conversionLOWER/UPPERLOWER/UPPERLOWER/UPPERLOWER/UPPERLOWER/UPPER
sqlite
SELECT SUBSTR(product_name, 1, 12) AS short_name,       category || ': ' || product_name AS labelFROM product;
postgresql · standard-style substring
SELECT SUBSTRING(product_name FROM 1 FOR 12) AS short_name,       category || ': ' || product_name AS labelFROM product;
mysql
SELECT SUBSTRING(product_name, 1, 12) AS short_name,       CONCAT(category, ': ', product_name) AS labelFROM product;
sql server
SELECT SUBSTRING(product_name, 1, 12) AS short_name,       CONCAT(category, ': ', product_name) AS labelFROM product;

Even apparently similar operations can differ for trailing spaces, byte length, Unicode grapheme handling, or NULL concatenation. Test the exact data contract.

Date/time equivalents

IntentSQLitePostgreSQLMySQLSQL ServerOracle
Current dateDATE('now')CURRENT_DATECURRENT_DATECAST(GETDATE() AS date)CURRENT_DATE
Extract yearSTRFTIME('%Y',x)EXTRACT(YEAR FROM x)EXTRACT(YEAR FROM x) or YEAR(x)DATEPART(year,x)EXTRACT(YEAR FROM x)
Add daysDATE(x,'+7 days')x + INTERVAL '7 days'DATE_ADD(x, INTERVAL 7 DAY)DATEADD(day,7,x)x + 7 for DATE values
Format dateSTRFTIME(format,x)TO_CHAR(x,format)DATE_FORMAT(x,format)CONVERT/FORMAT with tradeoffsTO_CHAR(x,format)
Date arithmetic is not mechanically portable

Engines differ in timestamp types, time-zone semantics, interval types, daylight-saving transitions, end-of-month behavior, and invalid date handling.

NULL and conditional equivalents

IntentPortable choiceVendor alternatives
First non-NULL valueCOALESCE(a,b,...)SQLite IFNULL, MySQL IFNULL, SQL Server ISNULL, Oracle NVL
NULL when equalNULLIF(a,b)Widely supported with vendor-specific type details
Conditional valueCASEMySQL IF, SQLite IIF, Oracle DECODE for narrower cases
NULL testIS NULLDo not replace with ordinary equality
portable · prefer standard conditional forms
SELECT    COALESCE(NULLIF(TRIM(email), ''), '[no email]') AS email_label,    CASE        WHEN credit_limit IS NULL THEN 'review'        ELSE 'known'    END AS limit_stateFROM customer;

Vendor shortcuts can be useful in a single-engine system. Standard forms generally reduce migration effort and make intent clearer to a wider audience.

Conversion equivalents

portable core
SELECT CAST(unit_price AS INTEGER) AS whole_priceFROM product;
sql server · style-aware conversion
SELECT CONVERT(varchar(10), sold_at, 23) AS sold_dateFROM sale;
postgresql · cast shorthand
SELECT unit_price::integer AS whole_priceFROM product;

CAST is the most portable core syntax. Vendor forms may add formatting styles, tolerant conversions, or shorthand, but those features bind the query to that engine.

Engine featureExamplePortability concern
SQL Server tolerant conversionTRY_CAST / TRY_CONVERTReturns NULL instead of failing for many invalid conversions.
PostgreSQL shorthandx::typeConcise but non-standard.
Oracle formatting conversionTO_CHAR/TO_DATEFormat models are Oracle-specific.
MySQL coercionContext-dependent conversionPermissive modes can hide invalid data.
SQLite affinity conversionDynamic storage classesResult may differ from rigidly typed engines.

Function return types are part of portability

sqlite · inspect local behavior
SELECT    TYPEOF(ROUND(49.95, 1)) AS rounded_type,    TYPEOF(COALESCE(NULL, 0)) AS integer_fallback_type,    TYPEOF(COALESCE(NULL, 0.0)) AS real_fallback_type,    TYPEOF(DATE('2026-08-05')) AS date_function_type;

SQLite's DATE function returns text. PostgreSQL, SQL Server, MySQL, and Oracle have dedicated temporal types and different expression type-resolution rules. Equivalent visible output does not imply equivalent result metadata.

Choose a portability architecture

StrategyAdvantagesCosts
One SQL dialectUse the engine fully and keep queries simpleMigration requires a deliberate rewrite.
Portable SQL subsetShared queries run across several enginesLowest-common-denominator features can reduce clarity or performance.
Dialect adapter layerEach engine receives idiomatic SQLMore implementations and test cases.
Query builder or ORMCentralizes syntax generationGenerated SQL still needs inspection and database expertise.
Database views/API boundaryExpose one stable contract to applicationsEngine-specific database objects must be maintained.
Portability is a product requirement

Do not pay permanent complexity for a hypothetical migration. Decide whether supporting multiple engines is an actual requirement, then test every supported dialect in continuous integration.

A practical compatibility matrix

text · record tested semantics
operation: normalized email labelsemantic rule: trim; blank becomes missing; fallback label is [no email]result type: textnull input: [no email]non-ASCII test: requiredsqlite expression: COALESCE(NULLIF(TRIM(email), ''), '[no email]')postgresql expression: COALESCE(NULLIF(BTRIM(email), ''), '[no email]')mysql expression: COALESCE(NULLIF(TRIM(email), ''), '[no email]')sql_server expression: COALESCE(NULLIF(TRIM(email), ''), '[no email]')oracle expression: COALESCE(NULLIF(TRIM(email), ''), '[no email]')

A matrix captures meaning, input cases, result metadata, and implementation—not merely function-name pairs.

Practice rewrite lab

  1. Write a product label in SQLite, PostgreSQL, MySQL, and SQL Server.
  2. Extract the sale year in each target dialect.
  3. Add seven days to a sale timestamp in each dialect.
  4. Use portable COALESCE, NULLIF, CASE, and CAST where possible.
  5. List tests for NULL, empty text, Unicode, leap dates, and invalid conversion input.
portable baseline
SELECT    CAST(product_id AS VARCHAR(20)) AS product_id_text,    COALESCE(NULLIF(TRIM(product_name), ''), '[unnamed]') AS product_label,    CASE WHEN stock_qty = 0 THEN 'unavailable' ELSE 'available' END AS stock_stateFROM product;

Common failures

Creating a function-name cheat sheet only

Names do not capture types, collations, NULL rules, and boundary behavior.

Assuming visible strings prove equivalence

Result metadata and time-zone meaning can still differ.

Mixing dialects in one query

A query containing PostgreSQL casts, MySQL date functions, and SQL Server TOP is not portable.

Choosing the lowest common denominator prematurely

This can make every implementation worse without a real multi-engine requirement.

Skipping cross-engine tests

Documentation comparison cannot replace execution against supported versions.

Summary and references

  • Portability includes names, syntax, types, and behavior.
  • Prefer standard CASE, COALESCE, NULLIF, and CAST when they express the requirement well.
  • Date/time and string behavior require special cross-engine testing.
  • Choose an architecture that matches the actual multi-database requirement.
  • Maintain a semantic compatibility matrix and automated tests.

References

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.