PostgreSQL Data Types

A practical guide to PostgreSQL data types — booleans, text, numbers, dates, JSON, arrays, and UUID — with examples and tips for choosing the right type.

5 min read · Back to overview

Quick Answer

PostgreSQL supports Boolean, character (CHAR, VARCHAR, TEXT), numeric (SMALLINT, INT, BIGINT, NUMERIC), temporal (DATE, TIME, TIMESTAMP, TIMESTAMPTZ, INTERVAL), UUID, JSON/JSONB, arrays, and more. Choosing the right type enforces data integrity and enables efficient indexing.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

Choosing the right PostgreSQL data type is one of the most important schema design decisions you can make. The correct type enforces data integrity at the database level, enables efficient indexing, and prevents entire classes of application bugs before they reach production.

Syntax

Column data types are declared in CREATE TABLE or added via ALTER TABLE:

CREATE TABLE table_name (
  column_name data_type [constraint],
  ...
);

Practical Example

The following table models IoT sensor readings and demonstrates a realistic mix of built-in types:

CREATE TABLE sensor_readings (
  reading_id  BIGSERIAL PRIMARY KEY,
  device_uuid UUID        NOT NULL DEFAULT gen_random_uuid(),
  is_active   BOOLEAN     NOT NULL DEFAULT TRUE,
  label       VARCHAR(100),
  notes       TEXT,
  temperature NUMERIC(6, 2),
  voltage     DOUBLE PRECISION,
  recorded_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  tags        TEXT[],
  payload     JSONB
);

Insert a sample row and retrieve it:

INSERT INTO sensor_readings (label, temperature, voltage, tags, payload)
VALUES (
  'Zone-A Rack 3',
  22.75,
  3.3,
  ARRAY['production', 'zone-a'],
  '{"firmware": "v3.0", "threshold": 25.0}'
)
RETURNING reading_id, device_uuid, recorded_at;

Inspect the table structure:

\d sensor_readings

Type Reference

CategoryTypesNotes
BooleanBOOLEANTRUE, FALSE, NULL; accepts 'yes'/'no'/1/0 input
CharacterCHAR(n), VARCHAR(n), TEXTTEXT and VARCHAR are performance-equivalent
IntegerSMALLINT, INT, BIGINT2-, 4-, and 8-byte signed integers
Exact numericNUMERIC(p,s), DECIMAL(p,s)Exact arithmetic; use for money
Floating-pointREAL, DOUBLE PRECISIONInexact; never use for money
TemporalDATE, TIME, TIMESTAMP, TIMESTAMPTZ, INTERVALPrefer TIMESTAMPTZ for event times
UUIDUUID128-bit identifier per RFC 4122
JSONJSON, JSONBJSONB is binary, indexable, preferred
Arraytype[]Every type can be used as an array

Testing with Vela

When refactoring a schema to fix type choices — for example, migrating a VARCHAR column that was wrongly used for monetary values to NUMERIC — Vela database branching lets you apply the migration on a copy of production data and validate query results before touching the live database. Create a branch, run the ALTER TABLE … ALTER COLUMN … TYPE NUMERIC USING migration, confirm no precision is lost, then promote the change to production with confidence.

Production Tips

  • Use TIMESTAMPTZ instead of TIMESTAMP for event-time columns to eliminate time-zone ambiguity.
  • Never use REAL or DOUBLE PRECISION for money — use NUMERIC(p,s) for exact arithmetic.
  • Prefer TEXT over VARCHAR(n) unless you have a real business constraint on length; a CHECK constraint is more explicit than a hidden VARCHAR limit.
  • Use UUID primary keys when rows are created by distributed clients or when sequential IDs must not be guessable from URLs.
  • Use JSONB over JSON so you can create GIN indexes and use containment operators like @>.
  • Use BIGSERIAL or BIGINT GENERATED ALWAYS AS IDENTITY for high-volume tables to avoid exhausting the INTEGER range (~2 billion rows).

Continue in Managing Tables: Create Table.

Related in this section: Create Table · Select Into · Create Table As

Frequently Asked Questions

What is the difference between VARCHAR and TEXT in PostgreSQL?
VARCHAR(n) and TEXT have identical storage and performance. The only difference is that VARCHAR(n) enforces a maximum-length constraint. Use TEXT when no length limit is needed, or express the limit more explicitly with a CHECK constraint.
When should I use TIMESTAMP vs TIMESTAMPTZ?
Use TIMESTAMPTZ (timestamp with time zone) for any column recording real-world events. PostgreSQL stores it as UTC and converts to the session time zone on output, preventing ambiguity across deployments in multiple regions.
Does choosing the wrong numeric type affect performance?
Yes. Using NUMERIC/DECIMAL for columns that do not need exact arithmetic is slower than BIGINT because NUMERIC calculations are done in software. Reserve NUMERIC for monetary or scientific values requiring exact results; use integer types for counts and IDs.
Should I use JSON or JSONB in PostgreSQL?
Prefer JSONB for most use cases. JSONB stores data in a parsed binary format, supports GIN indexing, and allows efficient containment queries. JSON stores raw text and must be re-parsed on every read.
What is the best data type for storing monetary values?
Use NUMERIC(p,s) for monetary values. It provides exact arithmetic and avoids the rounding errors of floating-point types like REAL and DOUBLE PRECISION. For example, NUMERIC(12,2) stores values up to 9,999,999,999.99 with exact cents.