Q1Which of the following is a correct benefit of storing dates as ISO strings like '2024-03-15'?
Date/Time Types and STRICT Tables
This article is part of the SQL Course, where you master practical SQL skills from scratch, from the fundamentals through to complex queries and SQL tuning.
Using the 50,000-row perf_sales table, learn the three date storage formats (ISO strings, julianday, unixepoch), year-based and range aggregation with date() / strftime(), and how the typed_strict table rejects type mismatches — all run live in the browser.
The data we'll use — perf_sales
Dates are stored as either strings or numbers, and you read or compute them with functions like date() / strftime().
In the first half, you'll use the sale_date column of the perf_sales sales table to check the three storage formats and how to extract values; in the second half, you'll create a STRICT table yourself to see what happens when you insert a value that doesn't match the declared type.
The three date storage formats — ISO string / julianday / unixepoch
Dates and times can be stored in three main formats.
| Format | Example value | Main use |
|---|---|---|
| ISO string | '2024-03-15' (with time: '2024-03-15 12:30:00') | Human-readable; sorting and range comparison work correctly as plain string comparison (the most common choice) |
| julianday | julianday('2024-03-15') → 2460384.5 (a decimal counting days since antiquity) | Computing the difference between two dates in days |
| unixepoch | strftime('%s', ...) → 1710460800 (integer seconds since 1970-01-01) | Second-level time math / interoperating between systems |
Whichever format you store, you can read it back as 'YYYY-MM-DD' with date() or as 'YYYY-MM-DD HH:MM:SS' with datetime().
This course uses the ISO string by default because it's human-readable and easy to compare, and only converts to julianday / unixepoch when needed.
-- The same date in 3 formats (read-only example)
SELECT '2024-03-15' AS iso_text,
julianday('2024-03-15') AS as_julianday,
strftime('%s','2024-03-15 00:00:00') AS as_unixepoch;
-- Convert each of the 3 formats back to a readable date
SELECT date(julianday('2024-03-15')) AS from_jd,
datetime(1710460800,'unixepoch') AS from_unix,
date('2024-03-15') AS from_iso;
-- With ISO strings, range comparison works correctly as plain string comparison
SELECT '2024-03-15' BETWEEN '2024-01-01' AND '2024-12-31' AS in_2024; -- 1
Manipulate and aggregate dates with date() and strftime()
date(value, modifier...): returns the date part as'YYYY-MM-DD', and lets you do date math with modifiers like'+1 month'/'-7 days'/'start of month'strftime(format, value): extracts a piece of the date as a string using format specifiers like%Y(year),%m(month),%d(day),%w(day of week)- Combine
strftime('%Y', sale_date)withGROUP BYto aggregate by year - Because
sale_dateis stored as an ISO string, range conditions like `WHERE sale_date >= '2024-01-01'` work correctly as plain string comparisons
-- Date manipulation (read-only example)
SELECT date('2024-03-15','+1 month') AS plus_1_month, -- 2024-04-15
date('2024-03-15','start of month') AS month_start, -- 2024-03-01
strftime('%Y', '2024-03-15') AS year_part, -- 2024
strftime('%Y-%m','2024-03-15') AS year_month; -- 2024-03
-- Count by year (ISO string, so extract the year with strftime and GROUP BY)
SELECT strftime('%Y', sale_date) AS yr, COUNT(*) AS cnt
FROM perf_sales
GROUP BY yr
ORDER BY yr;
STRICT tables — enforce strict typing
The type affinity we've seen so far flexibly converts numeric-looking strings.
By contrast, when you add STRICT at the end of a table definition, that table rejects storing values whose type doesn't match the declared one.
If you try to insert a non-numeric string into a numeric column, the value isn't converted implicitly — you get an error.
Use this for tables where you want to catch type mix-ups early.
In a STRICT table, the allowed column types are limited to INTEGER / REAL / TEXT / BLOB / ANY — INT and custom type names aren't allowed.
In the next exercise you'll create a typed_strict table with STRICT yourself, and in the following exercises you'll insert both matching and mismatched values to see the difference.
-- Declaring and using a STRICT table (read-only example)
CREATE TABLE IF NOT EXISTS typed_strict(a INTEGER, b TEXT, c REAL) STRICT;
-- Rows with matching types insert just fine
INSERT INTO typed_strict VALUES (1, '2024-03-15', 1.5);
-- Inserting a non-numeric string into the a INTEGER column
-- is not implicitly converted -- the INSERT errors out:
-- cannot store TEXT value in INTEGER column typed_strict.a
INSERT INTO typed_strict VALUES ('not-a-number', 'x', 1.0);
Knowledge Check
Answer each question one by one.
Q2Which of the following correctly describes the role of strftime('%Y', sale_date)?
Q3Which of the following correctly describes what happens when you try to INSERT a non-numeric string into an INTEGER column of a STRICT table?