✳ OMNIVIEWER SQLite viewer SQLite generator ← back

SQLite INTEGER, explained

INTEGER is the type SQLite is best at: a whole number stored in as few bytes as it needs, and — as INTEGER PRIMARY KEY — the key every table is physically sorted by. Here’s how it’s stored, what it can hold, and the traps.

On this page In one paragraph How it’s stored INTEGER PRIMARY KEY Examples Limits Gotchas Affinity rules Other types

In one paragraph (ELI12)

An INTEGER is a whole number — 7, -3, 2026 — with no decimal point. SQLite is thrifty with them: the numbers 0 and 1 cost zero bytes of data, a number up to 127 costs one byte, and the biggest numbers cost eight. So a column full of small counts or yes/no flags takes almost no space.

How it’s stored

Each row is a record: a header listing one serial type per column, then the column bodies. For integers the serial type says how many bytes follow, big-endian two’s complement:

Serial typeBytesRange
80the value 0
90the value 1
11-128 … 127
22-32,768 … 32,767
33-8,388,608 … 8,388,607
44-2,147,483,648 … 2,147,483,647
56±140,737,488,355,327
68-9,223,372,036,854,775,808 … 9,223,372,036,854,775,807

The size is chosen per value, not per column: declaring BIGINT or SMALLINT changes nothing on disk. Here’s how our generator picks the serial type:

function intSerial(v) {
  if (v === 0) return 8;           // zero bytes
  if (v === 1) return 9;           // zero bytes
  const a = v < 0 ? -v - 1 : v;
  if (a < 0x80) return 1;          // 1 byte
  if (a < 0x8000) return 2;        // 2 bytes
  if (a < 0x800000) return 3;      // 3 bytes
  if (a < 0x80000000) return 4;    // 4 bytes
  if (a < 0x800000000000) return 5; // 6 bytes
  return 6;                        // 8 bytes
}

INTEGER PRIMARY KEY is the rowid

Every ordinary table is a b-tree sorted by a hidden 64-bit key, the rowid. Declare a column exactly INTEGER PRIMARY KEY and it becomes that key — an alias, stored once, in the b-tree cell rather than the record. Looking a row up by it is one root-to-leaf descent: three or four page reads even in a 20 GB table. That’s how the viewer scrolls to row 180,000,000 instantly: WHERE rowid >= ? LIMIT 30.

Examples

CREATE TABLE t (id INTEGER PRIMARY KEY, n INTEGER, big BIGINT, flag INT);
INSERT INTO t (n, big, flag) VALUES ('42', 9007199254740993, 1);
INSERT INTO t (n, big, flag) VALUES ('4.0', '12abc', 2.5);

SELECT id, n, typeof(n), big, typeof(big), flag, typeof(flag) FROM t;
-- 1 | 42 | integer |  9007199254740993 | integer | 1   | integer
-- 2 | 4  | integer |  12abc            | text    | 2.5 | real

The text '42' and even '4.0' were converted to integers, because they look like numbers that fit losslessly. '12abc' doesn’t, so it’s kept as TEXT — in an INTEGER column. And 2.5 stays REAL because converting it would lose the half.

Limits

Gotchas

Storage class vs. declared type vs. affinity

SQLite is dynamically typed: the type belongs to the value, not the column. Every value is one of five storage classes — NULL, INTEGER, REAL, TEXT, BLOB. A column’s declared type only sets its affinity: a preference SQLite applies when you store a value, by five rules checked in order:

#If the declared type…AffinityExamples
1contains INTINTEGERINT, INTEGER, BIGINT, SMALLINT, UNSIGNED BIG INT, INT8
2contains CHAR, CLOB or TEXTTEXTTEXT, VARCHAR(255), CHARACTER(20), NCHAR(55), CLOB
3contains BLOB, or is emptyBLOBBLOB, (no type)
4contains REAL, FLOA or DOUBREALREAL, DOUBLE, DOUBLE PRECISION, FLOAT
5anything elseNUMERICNUMERIC, DECIMAL(10,5), BOOLEAN, DATE, DATETIME

Order matters: CHARINT matches rule 1 before rule 2 and is INTEGER; FLOATING POINT contains INT and is INTEGER too. The viewer implements exactly these rules:

function affinityOf(declType) {
  const t = String(declType || '').toUpperCase();
  if (t.includes('INT')) return 'INTEGER';
  if (t.includes('CHAR') || t.includes('CLOB') || t.includes('TEXT')) return 'TEXT';
  if (t.includes('BLOB') || t.trim() === '') return 'BLOB';
  if (t.includes('REAL') || t.includes('FLOA') || t.includes('DOUB')) return 'REAL';
  return 'NUMERIC';
}

The other types

INTEGERwhole numbers, 0 to 8 bytes, and the rowid REAL8-byte floating point, and why 0.1 + 0.2 ≠ 0.3 TEXTUTF-8 strings, VARCHAR(n) that isn’t, collations BLOBraw bytes, untyped columns, and STRICT’s ANY NUMERICdates, booleans and DECIMAL(10,2)

Open any .sqlite, .db or .sqlite3 file in the SQLite viewer — every column header has an ELI12 button that explains its type and links here. How the viewer reads a 20 GB database without loading it: how to open a 20 GB SQLite database in the browser.