Integer Data Types in PostgreSQL

Learn about PostgreSQL SMALLINT, INTEGER, and BIGINT: storage sizes, value ranges, unsigned integer alternatives, and how to choose the right type for your use case.

4 min read · Back to overview

Quick Answer

PostgreSQL provides three integer types: SMALLINT (2 bytes, ±32,767), INTEGER (4 bytes, ±2.1 billion), and BIGINT (8 bytes, ±9.2 quintillion). Use INTEGER for most cases; use BIGINT proactively for primary keys on high-volume tables.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

PostgreSQL provides three integer types for storing whole numbers: SMALLINT, INTEGER, and BIGINT. Choosing the right type balances storage efficiency, value range, and query performance. All three types reject fractional inputs — only whole numbers are accepted.

Syntax

column_name SMALLINT       -- 2 bytes
column_name INTEGER        -- 4 bytes; aliases: INT, INT4
column_name BIGINT         -- 8 bytes; alias: INT8
TypeStorageRange
SMALLINT2 bytes-32,768 to +32,767
INTEGER / INT4 bytes-2,147,483,648 to +2,147,483,647
BIGINT8 bytes-9,223,372,036,854,775,808 to +9,223,372,036,854,775,807

Practical Example

Use all three integer types in an e-commerce schema:

-- SMALLINT for bounded small values
CREATE TABLE product_ratings (
  product_id  INTEGER   NOT NULL,
  user_id     BIGINT    NOT NULL,
  rating      SMALLINT  NOT NULL CHECK (rating BETWEEN 1 AND 5),
  PRIMARY KEY (product_id, user_id)
);

-- INTEGER for standard quantities
CREATE TABLE orders (
  id              SERIAL PRIMARY KEY,
  product_id      INTEGER NOT NULL,
  quantity        INTEGER NOT NULL CHECK (quantity > 0),
  unit_price_cents INTEGER NOT NULL CHECK (unit_price_cents > 0)
);

-- BIGINT for high-volume event identifiers
CREATE TABLE events (
  id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  order_id    INTEGER     NOT NULL,
  event_type  VARCHAR(50) NOT NULL,
  occurred_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

Insert data and verify type enforcement:

INSERT INTO product_ratings (product_id, user_id, rating)
VALUES (42, 100001, 5);

-- Attempting to insert a value out of SMALLINT range:
INSERT INTO product_ratings (product_id, user_id, rating)
VALUES (43, 100002, 6);
-- ERROR: integer out of range (value 6 exceeds CHECK, not type range)

-- Attempting to insert a value exceeding SMALLINT storage:
INSERT INTO product_ratings (product_id, user_id, rating)
VALUES (44, 100003, 32768);
-- ERROR: smallint out of range

Inspect the column definitions:

\d+ orders

No Unsigned Integers — Use CHECK Instead

PostgreSQL does not have unsigned integer types. Enforce non-negative values with a CHECK constraint:

ALTER TABLE orders
ADD CONSTRAINT chk_quantity_positive CHECK (quantity > 0);

Testing with Vela

Before migrating an INTEGER primary key column to BIGINT on a production table, create a database branch, apply the ALTER TABLE ... ALTER COLUMN TYPE BIGINT migration, and run your full application test suite — ORM queries, foreign key joins, and index scans — against the branch to confirm nothing breaks before touching the live database.

Production Tips

  • Default to INTEGER for new columns — it covers nearly all practical integer values with minimal storage.
  • Use BIGINT proactively for auto-increment primary keys on high-volume tables to avoid a painful migration when INTEGER hits its 2.1 billion limit.
  • Use SMALLINT only when storage is a genuine concern and the value is provably bounded — for example, days of the week (0–6) or HTTP status codes.
  • PostgreSQL does not support unsigned integers — enforce non-negative constraints with CHECK (col >= 0) where needed.
  • Monitor sequence current values on high-write tables: SELECT last_value FROM table_id_seq; to catch impending overflow before it becomes an incident.

Continue in PostgreSQL Data Types: SERIAL Data Type.

Related in this section: Boolean · CHAR, VARCHAR, and TEXT · NUMERIC

Frequently Asked Questions

What are the PostgreSQL integer types and their ranges?
PostgreSQL has three integer types: SMALLINT (2 bytes, -32,768 to +32,767), INTEGER or INT (4 bytes, -2,147,483,648 to +2,147,483,647), and BIGINT (8 bytes, -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807). All three store whole numbers only.
Does inserting into an integer column lock the table?
No. Normal INSERT and UPDATE operations on integer columns acquire only row-level locks. Table-level locks are only taken during DDL operations such as ALTER TABLE to change the column type.
What happens to dependent sequences and serial columns when I change an INTEGER column to BIGINT?
If the column is backed by a SERIAL or sequence, you must also alter the sequence's data type: ALTER SEQUENCE seq_name AS bigint. Otherwise the sequence continues generating INTEGER-range values even though the column can hold BIGINT values.
Does PostgreSQL support unsigned integers?
No. Unlike MySQL, PostgreSQL does not have unsigned integer types. All integer types are signed. To enforce non-negative values, use a CHECK constraint: CHECK (quantity >= 0).
What is the safest way to migrate an INTEGER primary key to BIGINT in production?
On PostgreSQL 11+, ALTER TABLE t ALTER COLUMN id TYPE BIGINT can often complete without a full rewrite when the column has no generated expression. Test the migration on a Vela branch first, then apply during a low-traffic window since it acquires an ACCESS EXCLUSIVE lock.