Working with SQL Data Types

1. Using Integer Types

TypeRangeBytes
SMALLINT-32,768 to 32,7672
INTEGER / INT±2.1 billion4
BIGINT±9.2 × 10¹⁸8
SERIAL / IDENTITYAuto-increment INT4 / 8
UNSIGNED (MySQL)0 to 2× positive rangesame
Note: Use BIGINT for primary keys in growing systems — INT overflow at 2.1B rows is a real outage scenario.

2. Using Decimal Types

TypePrecisionUse
NUMERIC(p,s) / DECIMAL(p,s)Exact, arbitraryMoney, accounting
REAL / FLOAT~6 digits, 4 bytesScientific, approximate
DOUBLE PRECISION~15 digits, 8 bytesScientific, higher precision
MONEY (Postgres)Fixed 2 decimalsCurrency (locale-dependent — prefer NUMERIC)
Warning: Never use FLOAT/DOUBLE for currency — rounding errors compound. Use NUMERIC(p,s).

3. Using String Types

TypeDescription
CHAR(n)Fixed-length, space-padded
VARCHAR(n)Variable up to n
TEXTUnlimited variable (Postgres same speed as VARCHAR)
CITEXT (Postgres)Case-insensitive text
CLOB / LONGTEXTVery large strings (MySQL, Oracle)

4. Using Date and Time Types

TypeStores
DATEYear-Month-Day
TIME [WITH TIME ZONE]Time of day
TIMESTAMPDate + time (no tz)
TIMESTAMPTZ / TIMESTAMP WITH TIME ZONEUTC-stored, tz-aware
INTERVALDuration (e.g., '3 days 2 hours')

Example: Time Zone-Safe Storage

CREATE TABLE events (
  id        BIGSERIAL PRIMARY KEY,
  starts_at TIMESTAMPTZ NOT NULL,
  duration  INTERVAL    NOT NULL
);
INSERT INTO events(starts_at, duration)
VALUES ('2026-05-20 14:00 America/New_York', '90 minutes');

5. Using Boolean Types

DBTypeLiterals
PostgresBOOLEANTRUE/FALSE, 't'/'f', 1/0
MySQLTINYINT(1) alias BOOLEAN0/1
SQL ServerBIT0/1, NULL
OracleNUMBER(1) or CHAR(1)No native boolean prior to 23ai

6. Using Binary Types

TypeDescription
BYTEA (Postgres)Variable-length byte array
BLOB / VARBINARY (MySQL)Binary large objects
VARBINARY(MAX) (SQL Server)Variable binary up to 2 GB
RAW / BLOB (Oracle)Binary types
Note: Store large binary (images, PDFs) in object storage (S3, GCS) and keep only references in the DB.

7. Using JSON and XML Types

TypeIndexableNotes
JSON (Postgres)LimitedStores raw text, preserves whitespace
JSONB (Postgres)GIN, BTREE on expressionsBinary, faster queries, recommended
JSON (MySQL 8+)Functional indexesBinary internally
XMLXPath/XQueryLegacy; rarely needed today

Example: JSONB Query

CREATE TABLE products (id BIGSERIAL PK, data JSONB);
CREATE INDEX idx_data_brand ON products ((data->>'brand'));
SELECT id FROM products WHERE data @> '{"brand":"Acme","in_stock":true}';

8. Using Array and Composite Types

FeaturePostgres Syntax
Array columntags TEXT[]
Array literalARRAY['a','b'] or '{a,b}'
Contains operatortags @> ARRAY['sale']
Composite typeCREATE TYPE addr AS (street TEXT, zip TEXT);
UnnestSELECT UNNEST(tags) FROM products;

9. Creating User-Defined Types

TypeExample
ENUMCREATE TYPE status AS ENUM ('open','closed');
DOMAINCREATE DOMAIN email_t AS TEXT CHECK (VALUE ~ '@');
COMPOSITECREATE TYPE point AS (x FLOAT, y FLOAT);
RANGECREATE TYPE pricerange AS RANGE (subtype = NUMERIC);

10. Choosing Appropriate Data Types

DataRecommended
CurrencyNUMERIC(15,2) or smallest_unit BIGINT (cents)
IdentifierBIGSERIAL or UUID v7
TimestampTIMESTAMPTZ (always UTC)
EmailCITEXT or VARCHAR(254)
Boolean flagBOOLEAN
Enum-likeENUM type or VARCHAR + CHECK
Flexible attrsJSONB
GeographicPostGIS GEOMETRY/GEOGRAPHY