Compare

INT vs BIGINT vs SMALLINT

Short answer: SMALLINT is 2 bytes (~±32K), INT is 4 bytes (~±2.1B), BIGINT is 8 bytes (~±9.2 quintillion). Use INT for almost everything; use BIGINT for IDs on tables that might exceed 2 billion rows or for monetary values stored as cents on accounts that might cross billion-dollar lifetime totals; use SMALLINT only for genuinely small ranges where the storage savings matter (rare on modern engines).

Quick comparison #

SMALLINTINT / INTEGERBIGINT
Bytes248
Range-32,768 to 32,767-2.1B to 2.1B-9.2 quintillion to 9.2 quintillion
Unsigned variant (MySQL)0 to 65,5350 to 4.3B0 to 1.8e19
Use forsmall enumerations, ages, counters where storage mattersthe default for IDs, quantities, countsrow IDs on huge tables, microsecond timestamps, monetary amounts in cents
Default in modern schemas?rareyesfor primary keys on growth-likely tables

See the storage difference #

Loading SQL editor...

When to use which #

INT is the default. Foreign keys, counts, quantities, ages, anything that comfortably fits in ±2.1B. The 4-byte size matches CPU register size on most architectures, which gives it slight performance advantages on bulk operations.

BIGINT — when 2.1 billion is plausibly close:

  • Surrogate primary keys on event-style tables (logs, clicks, transactions). SERIAL (4-byte) on a write-heavy table runs out faster than people expect.
  • Money stored as integer minor units (cents): amount_cents BIGINT survives any account size; amount_cents INT caps at $21.4M.
  • Microsecond/nanosecond timestamps stored as integers.
  • Hash-derived IDs that need 64 bits.

SMALLINT — useful for genuinely small enumerations on huge tables where storage matters: a status code (1-10), a year-of-life column (0-150), a country index (200 values). Modern engines store INT efficiently enough that SMALLINT savings are usually negligible. Reach for it deliberately, not by default.

TINYINT / INT2 / INT8 are engine-specific aliases. TINYINT is 1 byte (0-255 unsigned, ±127 signed) on MySQL and SQL Server; doesn't exist in Postgres.

Common mistakes #

  • INT primary keys on growth tables: the canonical "we'll fix it before we hit the limit" promise. By the time the table hits 1.5B rows, the ALTER TABLE to widen the key takes hours and locks writers. Default to BIGINT for any table where 2B rows is plausible (events, audit logs, transactions).
  • INT for cents on monetary columns: INT caps at $21,474,836.47. Fine for individual transactions, dangerous for cumulative balances or aggregated revenue. Use BIGINT for cents, or use DECIMAL and skip the integer-cents trick. See DECIMAL vs FLOAT.
  • Mixing INT and BIGINT in joins: silent type promotion to BIGINT works but breaks index usage on some older engines. Match the types on FK columns.
  • Using unsigned INT to "double the range" on MySQL: works, but only MySQL has unsigned types, so moving the schema to Postgres or Snowflake later requires conversion. Better to use BIGINT from the start.

Dialect notes #

  • Postgres: SMALLINT (2 bytes), INTEGER/INT/INT4 (4 bytes), BIGINT/INT8 (8 bytes). SERIAL, BIGSERIAL for auto-increment. No unsigned types.
  • MySQL: TINYINT (1B), SMALLINT (2B), MEDIUMINT (3B — 24-bit), INT (4B), BIGINT (8B). Each has unsigned variant. AUTO_INCREMENT requires the column to be a key.
  • SQL Server: TINYINT (1B, 0-255 only — no signed), SMALLINT, INT, BIGINT. IDENTITY(seed, increment) for auto-increment.
  • SQLite: dynamic typing, so all integer types are stored as one variable-length INTEGER. The declared type is hint-only.
  • Snowflake: all integer types (TINYINT, SMALLINT, INT, BIGINT) are aliases for NUMBER(38,0). No real storage difference. Auto-increment via AUTOINCREMENT or IDENTITY.
  • BigQuery: only INT64 (8 bytes). Synonyms INTEGER, INT, SMALLINT, BIGINT are accepted but all map to INT64.
  • DuckDB: TINYINT through HUGEINT (16 bytes). Auto-increment via SEQUENCE.

FAQ #

Should I use INT or BIGINT for a primary key?

BIGINT for any table that might grow large: events, transactions, audit logs, analytics tables. The 4-byte saving from INT is invisible on small tables and irrelevant on large ones. Migrating an INT PK to BIGINT on a billion-row table takes hours of locked writers and a deployment risk you didn't need to take.

When is SMALLINT actually useful?

When you have a column with a genuinely small range (status code 1-10, year-of-life 0-150, country index up to 200) on a huge table where every byte matters. Modern engines store INT efficiently enough that SMALLINT savings are usually negligible. Reach for it deliberately, not by default.

Can I store cents/pence as an integer instead of DECIMAL?

Yes, and many financial systems do. Use BIGINT (not INT) for cents to avoid the $21M cap. The benefit: no rounding errors. The downside: you have to remember the scale (always divide by 100 for display) and reasoning about partial cents (split bills, taxes) becomes harder. DECIMAL(15, 2) is usually simpler.

What's the maximum value for an INT?

Signed: ±2,147,483,647 (about 2.1 billion). Unsigned (MySQL): 0 to ~4.3 billion. BIGINT signed: ±9.2 quintillion. SMALLINT signed: ±32,767. Choose by what fits the column's domain plus a safety margin for growth.

Are there performance differences between INT and BIGINT?

Marginal on modern hardware. CPUs handle both natively. Storage is 4 bytes vs 8 bytes, a 2x difference per row but rarely material vs the rest of the row. Indexing performance is functionally equivalent. Don't pick INT over BIGINT for performance reasons; pick by domain fit.

What about TINYINT?

1-byte integer. MySQL: TINYINT (-128 to 127, or 0-255 unsigned). SQL Server: TINYINT (0-255 only — no signed). Postgres: no TINYINT; use SMALLINT. Useful for boolean-like flags or genuinely small ranges; on small tables the byte savings are negligible.

Tip

**Default to BIGINT for primary keys on any table that might grow large.** The 4-byte saving from INT is invisible on small tables and irrelevant on large ones (compared to the rest of the row). The cost of migrating from INT to BIGINT on a billion-row table is hours of locked writers and a deployment risk you didn't need to take. The cost of choosing BIGINT upfront is zero. The asymmetry strongly favors going wide from day one.