Chapter 06 · Functions, NULLs, and Conditional Logic

String, Numeric, Date, and Conversion Functions

Use scalar functions as precise value transformations, not as mysterious shortcuts, and verify what each function returns.

Beginner90–110 minutesScalar functions + SQLite labLast reviewed: August 2026

Learning outcomes

A scalar function receives one row's values and returns one value for that row. The function call is an expression, so it can appear in the select list, predicates, ordering, and many other expression positions.

01

Distinguish string, numeric, date/time, and conversion functions by input and return value.

02

Compose functions while keeping NULL behavior and data types visible.

03

Use SQLite date/time functions with explicit storage conventions.

04

Prefer explicit conversion over accidental implicit coercion.

A function is a value transformation

Input expression
Function evaluates
Return value obtains a type
Alias names the result

Functions transform values; they do not automatically clean the stored source. The returned value becomes part of the query result unless a data-change statement stores it.

sqlite · one transformation per output column
SELECT    customer_id,    TRIM(full_name) AS clean_name,    LOWER(email) AS normalized_email,    ROUND(credit_limit, 0) AS rounded_limitFROM customerORDER BY customer_id;

String functions

FunctionPurposeImportant detail
TRIM(x)Remove leading and trailing charactersDefault behavior removes spaces; vendor options differ.
UPPER(x) / LOWER(x)Change letter caseResults depend on character set, collation, and engine capabilities.
LENGTH(x)Measure text length in SQLiteOther engines may distinguish characters from bytes.
SUBSTR(x,start,length)Extract part of a stringIndexing and spelling vary by dialect.
REPLACE(x,old,new)Replace matching textThis is literal replacement, not regular-expression replacement.
||Concatenate in SQLite and standard-style SQLMySQL and SQL Server often use different idioms.
sqlite · normalize presentation text
SELECT    customer_id,    TRIM(full_name) AS clean_name,    UPPER(city) AS city_upper,    LOWER(NULLIF(email, '')) AS normalized_email,    LENGTH(TRIM(full_name)) AS name_length,    SUBSTR(TRIM(full_name), 1, 5) AS name_prefixFROM customerORDER BY customer_id;
Normalization is contextual

Lowercasing an email for comparison can be useful, but changing stored names, identifiers, or multilingual text without a defined rule can lose meaning.

Numeric functions and arithmetic

sqlite · calculate and round monetary output
SELECT    p.product_name,    p.unit_price,    s.quantity,    ABS(s.quantity) AS absolute_quantity,    ROUND(p.unit_price * s.quantity, 2) AS gross_amount,    ROUND(p.unit_price * s.quantity * COALESCE(s.discount_rate, 0), 2)        AS discount_amountFROM sale AS sJOIN product AS p ON p.product_id = s.product_idORDER BY s.sale_id;

ROUND is appropriate for display-oriented decimal places, but it does not replace a deliberate monetary storage model. Floating-point values can still have binary representation limits.

OperationQuestion to ask
ABS(x)Is the sign meaningful, or are you hiding an invalid negative value?
ROUND(x,n)At which business boundary should rounding occur?
DivisionCan the divisor be zero or NULL?
ModuloHow does the target engine define signs and numeric types?
Math extensionsIs the function compiled or enabled in the deployed engine?

Date and time functions in SQLite

SQLite has no dedicated date/time storage class. A project must choose a convention such as UTC ISO-style text, Unix seconds, or Julian day values and use it consistently.

sqlite · derive calendar fields
SELECT    sale_id,    sold_at,    DATE(sold_at) AS sold_date,    TIME(sold_at) AS sold_time,    STRFTIME('%Y-%m', sold_at) AS sold_month,    STRFTIME('%w', sold_at) AS weekday_numberFROM saleORDER BY sale_id;
sqlite · shift and compare dates
SELECT    customer_id,    joined_at,    DATE(joined_at, '+30 days') AS first_review_date,    ROUND(JULIANDAY('2026-08-05') - JULIANDAY(DATE(joined_at)), 0)        AS membership_daysFROM customerORDER BY customer_id;
Time-zone policy belongs in the data contract

Do not assume that a timestamp string represents local time or UTC. Store the convention explicitly and convert at controlled system boundaries.

Conversion with CAST

portable form · explicit conversion
SELECT    product_id,    unit_price,    CAST(unit_price AS INTEGER) AS truncated_integer,    CAST(stock_qty AS TEXT) AS stock_textFROM productORDER BY product_id;

CAST(expression AS type) communicates an intended result type. Conversion can truncate, round, reject, or reinterpret data depending on the source value and target engine.

sqlite · inspect dynamic result types
SELECT    TYPEOF('42') AS literal_type,    TYPEOF(CAST('42' AS INTEGER)) AS cast_type,    CAST('42' AS INTEGER) + 8 AS numeric_result,    CAST(49.95 AS INTEGER) AS integer_result;

Predict before executing

  1. What value and type does CAST(49.95 AS INTEGER) produce in SQLite?
  2. What happens when LOWER receives NULL?
  3. Why should timestamp text use one sortable format?
Review the answers

SQLite truncates the fractional part when converting this positive REAL to INTEGER; most scalar functions propagate NULL; a consistent year-first format supports lexical ordering and predictable parsing.

Nested functions: work from the inside out

sqlite · nested transformations
SELECT    customer_id,    UPPER(TRIM(full_name)) AS display_name,    COALESCE(LOWER(NULLIF(TRIM(email), '')), '[no email]') AS email_label,    STRFTIME('%Y', DATE(joined_at)) AS joined_yearFROM customerORDER BY customer_id;

Read each expression from the innermost call outward. When nesting becomes difficult to explain, split the work into a subquery or another named query layer.

Functions in predicates can affect access paths

concept · transformed predicate
SELECT customer_id, emailFROM customerWHERE LOWER(email) = 'nadia@example.com';

The expression may be correct, but applying a function to every stored value can prevent a normal index on email from being used directly. Solutions include normalized stored values, expression indexes where supported, or a case-insensitive collation chosen deliberately. Index design is covered later in the course.

Practice lab

  1. Return trimmed customer names and lowercase email addresses.
  2. Return product prices rounded to zero and two decimal places.
  3. Return each sale date and month.
  4. Convert stock quantities to text and inspect their SQLite types.
  5. Create a label containing an uppercase category and product name.
sqlite · possible solutions
SELECT customer_id, TRIM(full_name), LOWER(email)FROM customer;SELECT product_id, ROUND(unit_price, 0), ROUND(unit_price, 2)FROM product;SELECT sale_id, DATE(sold_at), STRFTIME('%Y-%m', sold_at)FROM sale;SELECT product_id, CAST(stock_qty AS TEXT), TYPEOF(CAST(stock_qty AS TEXT))FROM product;SELECT UPPER(category) || ': ' || product_name AS product_labelFROM product;

Common failures

Assuming function names are portable

The concept may be portable while the spelling and argument order are not.

Cleaning only in the SELECT list

A pretty result does not repair inconsistent stored data.

Depending on implicit conversion

Different engines may choose different coercions or reject the expression.

Using local time without a policy

The same text can be interpreted differently across systems.

Rounding too early

Intermediate rounding can accumulate error and change totals.

Summary and references

  • Scalar functions transform one row's input expressions into result values.
  • String, numeric, date/time, and conversion functions have distinct type and NULL behavior.
  • SQLite date/time work requires a consistent storage convention.
  • Explicit casts make intended types visible.
  • Nested transformations should remain readable and testable.

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.