Working with Data Types
1. Using Integer Types
| Type | Size | Range |
|---|---|---|
| smallint | 2 B | -32,768 to 32,767 |
| integer / int | 4 B | ±2.1B |
| bigint | 8 B | ±9.2 quintillion |
2. Using Numeric Types
| Type | Use |
|---|---|
| numeric(p,s) | Exact, arbitrary precision (money, scientific) |
| real | 4-byte float |
| double precision | 8-byte float |
| money | Locale-aware (avoid; use numeric) |
CREATE TABLE prices (amount numeric(12,2) NOT NULL);
3. Using Serial Types
| Type | Underlying | Status |
|---|---|---|
| smallserial | smallint + sequence | LEGACY |
| serial | integer + sequence | LEGACY |
| bigserial | bigint + sequence | LEGACY |
Note: Prefer
GENERATED ALWAYS AS IDENTITY — SQL-standard and avoids permission quirks.4. Using Character Types
| Type | Notes |
|---|---|
| text | Unlimited length (preferred) |
| varchar(n) | Max n chars; no perf benefit vs text |
| char(n) | Blank-padded fixed (avoid) |
| citext | Case-insensitive (extension) |
5. Using Boolean Type
| Literal | Maps To |
|---|---|
| TRUE | 't', 'true', 'yes', 'on', '1' |
| FALSE | 'f', 'false', 'no', 'off', '0' |
| NULL | unknown |
6. Using Date and Time Types
| Type | Size | Notes |
|---|---|---|
| date | 4 B | Calendar day |
| time [(p)] | 8 B | Time of day, no TZ |
| timetz | 12 B | Avoid; no DST awareness |
| timestamp | 8 B | Without timezone |
| timestamptz | 8 B | Stored UTC; recommended |
7. Using INTERVAL Type
SELECT INTERVAL '1 year 2 months 3 days 04:05:06';
SELECT now() + INTERVAL '30 days';
SELECT EXTRACT(EPOCH FROM INTERVAL '1 day'); -- 86400
| Field | Example |
|---|---|
| Months | INTERVAL '3 months' |
| Days | INTERVAL '7 days' |
| Microseconds | INTERVAL '0.5 seconds' |
8. Using UUID Type
CREATE EXTENSION IF NOT EXISTS pgcrypto;
SELECT gen_random_uuid(); -- v4
-- PG 18+ has gen_uuid_v7() for time-ordered UUIDs
| Source | Function |
|---|---|
| pgcrypto | gen_random_uuid() |
| uuid-ossp | uuid_generate_v1mc(), uuid_generate_v4() |
9. Using JSON Types
| Type | Storage | Use |
|---|---|---|
| json | Verbatim text | Preserve formatting; rarely needed |
| jsonb | Binary, indexed | Default choice |
CREATE TABLE events (
id bigserial PRIMARY KEY,
payload jsonb NOT NULL
);
CREATE INDEX events_payload_gin ON events USING gin (payload jsonb_path_ops);
10. Using Array Types
CREATE TABLE posts (
id bigserial PRIMARY KEY,
tags text[] NOT NULL DEFAULT '{}'
);
INSERT INTO posts(tags) VALUES (ARRAY['sql','postgres','tips']);
SELECT * FROM posts WHERE 'sql' = ANY(tags);
CREATE INDEX posts_tags_gin ON posts USING gin(tags);
11. Using ENUM Types
CREATE TYPE order_status AS ENUM ('new','paid','shipped','cancelled');
ALTER TABLE orders ALTER COLUMN status TYPE order_status USING status::order_status;
ALTER TYPE order_status ADD VALUE 'refunded' AFTER 'cancelled';
| Op | Notes |
|---|---|
| ADD VALUE | Cannot run inside transaction block before PG 12 |
| Sort order | Definition order, not alphabetical |
| Rename | ALTER TYPE ... RENAME VALUE 'a' TO 'b' |
12. Using Composite Types
CREATE TYPE address AS (street text, city text, zip text);
CREATE TABLE people (id bigserial PRIMARY KEY, home address);
INSERT INTO people(home) VALUES (ROW('1 Main','Boston','02110'));
SELECT (home).city FROM people;
13. Using Domain Types
CREATE DOMAIN email_addr AS text
CHECK (VALUE ~* '^[^@]+@[^@]+\.[^@]+
Benefit Detail
Reuse Apply same CHECK to many columns
Centralize Change rule in one place
14. Using Range Types
Type Element
int4range / int8range integer / bigint
numrange numeric
tsrange / tstzrange timestamp / timestamptz
daterange date
CREATE TABLE reservations (
room_id int NOT NULL,
during tstzrange NOT NULL,
EXCLUDE USING gist (room_id WITH =, during WITH &&)
);
15. Using Network Types
Type Use
inet Host or network IPv4/IPv6 with optional CIDR
cidr Strict network (no host bits)
macaddr 6-byte MAC address
macaddr8 8-byte EUI-64 MAC
SELECT '192.168.1.10'::inet << '192.168.1.0/24'::cidr; -- true
);
CREATE TABLE users (id bigserial PRIMARY KEY, email email_addr NOT NULL);
| Benefit | Detail |
|---|---|
| Reuse | Apply same CHECK to many columns |
| Centralize | Change rule in one place |
14. Using Range Types
| Type | Element |
|---|---|
| int4range / int8range | integer / bigint |
| numrange | numeric |
| tsrange / tstzrange | timestamp / timestamptz |
| daterange | date |
CREATE TABLE reservations (
room_id int NOT NULL,
during tstzrange NOT NULL,
EXCLUDE USING gist (room_id WITH =, during WITH &&)
);15. Using Network Types
| Type | Use |
|---|---|
| inet | Host or network IPv4/IPv6 with optional CIDR |
| cidr | Strict network (no host bits) |
| macaddr | 6-byte MAC address |
| macaddr8 | 8-byte EUI-64 MAC |
SELECT '192.168.1.10'::inet << '192.168.1.0/24'::cidr; -- true