Chapter 02 · Tables, Schemas, and Data Types
Numeric, Character, Boolean, Date, Time, and Binary Types
Choose among the major SQL type families by understanding the values, operations, precision, and constraints each family represents.
Learning outcomes
A data type is not merely a storage size. It communicates a value’s domain, controls valid operations, influences comparisons and sorting, and helps the DBMS reject impossible data.
Distinguish exact numeric, approximate numeric, character, Boolean, temporal, and binary values.
Explain precision, scale, range, encoding, collation, and time-zone concerns.
Contrast SQLite storage classes and type affinity with strongly typed server databases.
Create a SQLite STRICT table and inspect the runtime type of stored values.
What a data type contributes
A sound type preserves meaning while enabling comparisons, calculations, indexing, serialization, and integrity checks.
A type answers several questions: Which values are permitted? How are they represented? Which operators and functions apply? How are values compared and sorted? What happens when an input cannot be converted?
| Type concern | Example question |
|---|---|
| Domain | Can this column contain only whole counts, or also fractions? |
| Range | Can the largest valid value fit? |
| Precision | Must 0.1 be represented exactly? |
| Ordering | Does lexical ordering match business ordering? |
| Operations | Should addition, date arithmetic, or pattern matching be valid? |
| Portability | Does the chosen type mean the same thing in the target engines? |
Numeric types: exact versus approximate
Integers and fixed-precision decimal types are exact within their supported range. Floating-point types are approximate binary representations designed for scientific measurements and wide dynamic ranges.
Integer
Whole values such as counts, sequence numbers, quantities, and identifiers. Common names include SMALLINT, INTEGER, and BIGINT.
Decimal / numeric
Exact base-10 values with declared precision and scale, commonly used for money and regulated calculations.
Floating point
Approximate values such as REAL, FLOAT, and DOUBLE PRECISION. Appropriate for measurements where tiny representation error is acceptable.
Precision + scale
For DECIMAL(p,s), precision is the total significant decimal digits and scale is the digits after the decimal point.
For a decimal declaration such as DECIMAL(12,2), the intent is up to twelve decimal digits in total, two of them after the decimal point. Exact limits and overflow behavior remain vendor-specific.
CREATE TABLE invoice_line ( invoice_line_id BIGINT PRIMARY KEY, quantity INTEGER NOT NULL, unit_price DECIMAL(12, 2) NOT NULL, measured_mass DOUBLE PRECISION, CHECK (quantity > 0), CHECK (unit_price >= 0));Values such as 0.1 cannot generally be represented exactly in binary floating point. Prefer an exact decimal type in server databases or an integer number of minor currency units when that model fits.
Character and text types
Character types hold textual data. Common declarations include CHAR(n), VARCHAR(n), and unbounded or large-text types such as TEXT. Their exact storage and length semantics vary by product.
CHAR(n)describes fixed-length character data and may pad values in some systems.VARCHAR(n)describes variable-length text with a declared limit.TEXTcommonly describes variable-length text without a small application-level limit.- Unicode support depends on the DBMS, database encoding, and type family. SQL Server, for example, distinguishes
VARCHARandNVARCHAR. - Collation controls comparison and ordering rules such as case, accents, and locale behavior.
| Stored text | Appropriate column idea | Poor alternative |
|---|---|---|
| Email address | Variable-length character text | Numeric or binary type |
| Country code | Short character text plus validation | Free-form paragraph |
| Article body | Large text | Thousands of numbered columns |
| Machine status code | Short text or constrained code | Human-readable sentence as the key |
Boolean types
A Boolean models two logical states: true and false. PostgreSQL has a native BOOLEAN type. Other systems use aliases or compact numeric types; SQLite stores Boolean-style values as integers, conventionally 1 and 0.
CREATE TABLE feature_flag ( flag_name TEXT PRIMARY KEY, is_enabled INTEGER NOT NULL CHECK (is_enabled IN (0, 1))) STRICT;INSERT INTO feature_flag VALUES ('new_catalogue', 1);-- This fails because 7 is not a valid flag value:INSERT INTO feature_flag VALUES ('unsafe_value', 7);The check constraint turns a broad integer storage class into the intended two-value domain. This illustrates an important design principle: the declared type and constraints work together.
Date and time types
Temporal values require more decisions than “store a date.” Identify the business concept:
| Business concept | Typical SQL family | Example |
|---|---|---|
| Calendar date | DATE | A birthday or billing date |
| Time of day | TIME | A store opens at 09:00 |
| Local date and time | TIMESTAMP / DATETIME | A wall-clock appointment |
| Global instant | Time-zone-aware timestamp or normalized UTC instant | An event occurred at one moment worldwide |
| Duration | Interval type or explicit numeric unit | A job ran for 850 milliseconds |
SQLite has no dedicated date/time storage class. It commonly stores ISO-8601 text, Julian day numbers, or Unix timestamps and supplies date/time functions to interpret them. Use one representation consistently and document its time-zone policy.
CREATE TABLE audit_event ( event_id INTEGER PRIMARY KEY, event_name TEXT NOT NULL, occurred_at TEXT NOT NULL CHECK (datetime(occurred_at) IS NOT NULL)) STRICT;INSERT INTO audit_event (event_name, occurred_at)VALUES ('course_opened', '2026-08-05T10:30:00Z');SELECT event_name, occurred_at, datetime(occurred_at)FROM audit_event;Binary types
Binary types store uninterpreted bytes. Typical uses include hashes, compact protocol payloads, encrypted values, signatures, and small file fragments. Names include BINARY, VARBINARY, BYTEA, RAW, and BLOB.
Do not choose a binary type merely because a value “looks technical.” A UUID, IP address, JSON document, or image might have a specialized native type, a validated textual representation, or an external object-storage location that better supports its operations.
CREATE TABLE checksum_sample ( sample_id INTEGER PRIMARY KEY, algorithm TEXT NOT NULL, digest BLOB NOT NULL) STRICT;INSERT INTO checksum_sample (algorithm, digest)VALUES ('demo', X'0A1B2C3D');SELECT algorithm, typeof(digest), length(digest), hex(digest)FROM checksum_sample;SQLite storage classes and affinity
SQLite associates a storage class with each value: NULL, INTEGER, REAL, TEXT, or BLOB. Ordinary tables use type affinity and may convert inputs when possible. This flexibility differs from the stronger type enforcement many server databases apply.
| SQLite concept | Meaning |
|---|---|
| Storage class | The runtime representation of one stored value |
| Declared type | The type name written in the table definition |
| Affinity | SQLite’s preference for converting and storing values in a column |
| STRICT table | An optional mode that restricts declared type names and rejects incompatible values |
STRICT tables support the core declarations INT, INTEGER, REAL, TEXT, BLOB, and ANY. They are excellent for this course because mistakes fail early while the environment remains lightweight.
Lab: inspect runtime types
DROP TABLE IF EXISTS type_lab;CREATE TABLE type_lab ( sample_id INTEGER PRIMARY KEY, whole_count INTEGER NOT NULL, measured_value REAL, description TEXT, raw_payload BLOB, flexible_value ANY) STRICT;INSERT INTO type_lab (whole_count, measured_value, description, raw_payload, flexible_value)VALUES (12, 19.75, 'first sample', X'CAFE', '00123'), (0, NULL, 'missing measurement', X'', 123);SELECT sample_id, typeof(whole_count) AS count_type, typeof(measured_value) AS measure_type, typeof(description) AS description_type, typeof(raw_payload) AS payload_type, typeof(flexible_value) AS flexible_typeFROM type_lab;-- Expected to fail in a STRICT table:INSERT INTO type_lab (whole_count) VALUES ('twelve');Run the query and compare the declared type with typeof(), which reports the runtime storage class. Notice that the ANY column preserves the difference between text '00123' and integer 123.
Extend the lab
- Add an
is_verifiedinteger column constrained to 0 or 1. - Add a textual UTC timestamp and validate it with
datetime(). - Insert
0.1 + 0.2into a REAL column and inspect the displayed result. - Attempt incompatible values in each STRICT column and record the error messages.
Common mistakes
Choosing a type from sample data only
Ten sample rows cannot establish the full range, precision, or future operations. Use business rules and expected growth.
Storing dates in locale-specific text
Values such as 08/05/26 are ambiguous. Use a native temporal type or a documented, sortable representation such as ISO 8601.
Using text for every value
Text can store almost anything but weakens validation and makes numeric, temporal, and Boolean operations harder or unsafe.
Assuming the same type name behaves identically everywhere
Length semantics, implicit conversions, ranges, time zones, and aliases vary. Portability requires testing, not just matching names.
Checkpoint and practice
Concept check
- Why is DECIMAL usually preferable to floating point for exact money?
- What is the difference between a calendar date and a global instant?
- How does a SQLite storage class differ from a declared column type?
- When is a binary type more appropriate than text?
Review the answers
Decimal arithmetic represents supported base-10 values exactly; floating point is approximate. A date identifies a day, while an instant identifies one point on the global timeline. SQLite stores a runtime class per value and applies affinity from the declaration. Binary types are appropriate when the value is fundamentally bytes and byte-level operations are required.
Summary and next lesson
Data-type families express more than storage: they preserve domains, enable valid operations, and support integrity. The next lesson turns these families into a repeatable selection process for real fields such as identifiers, money, measurements, dates, statuses, and flags.