Skip to main content

CREATE TABLE

Create a new table in the database with column definitions, data types, and constraints.

Syntax

CREATE TABLE [IF NOT EXISTS] [schema-name.]table-name (
    column-def [, column-def ...] [, table-constraint [, table-constraint ...]]
) [STRICT];

Column Definition

column-name [type-name] [column-constraint ...]

Column Constraints

PRIMARY KEY [ASC | DESC] [AUTOINCREMENT]
NOT NULL
UNIQUE
DEFAULT expression
CHECK (expression)
REFERENCES foreign-table [(column-name)] [foreign-key-actions]
COLLATE collation-name

Table Constraints

PRIMARY KEY (column-name [, column-name ...])
UNIQUE (column-name [, column-name ...])
CHECK (expression)
FOREIGN KEY (column-name [, column-name ...]) REFERENCES foreign-table [(column-name [, column-name ...])] [foreign-key-actions]

Description

CREATE TABLE defines a new table in the database. The table name must be unique within the database schema. Each table consists of one or more column definitions and optional table-level constraints.

Parameters

ParameterDescription
IF NOT EXISTSPrevents an error if a table with the same name already exists. The statement is a no-op when the table is present.
schema-nameThe name of an attached database. Defaults to the main database if omitted.
table-nameThe name of the table to create.
STRICTEnables strict type checking on the table. See STRICT Tables.

Column Definitions

Each column definition specifies a column name, an optional type, and zero or more column constraints. When no type is specified, the column accepts any storage class. When a type name is provided, it determines the column’s type affinity, which influences how inserted values are stored.
CREATE TABLE measurements (
    id INTEGER PRIMARY KEY,
    sensor_name TEXT NOT NULL,
    reading REAL,
    raw_data BLOB,
    notes                    -- no type: accepts any storage class
);

Column Constraints

PRIMARY KEY

Designates a column as the table’s primary key. A table can have at most one primary key.
CREATE TABLE users (
    id INTEGER PRIMARY KEY
);
When a column is declared as INTEGER PRIMARY KEY, it becomes an alias for the internal rowid, and inserted integer values are used as the row identifier directly. This is the most efficient primary key form.

AUTOINCREMENT

The AUTOINCREMENT keyword can only be used with INTEGER PRIMARY KEY. It prevents the reuse of rowid values from previously deleted rows by maintaining a counter in the sqlite_sequence system table.
CREATE TABLE events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    description TEXT
);
Without AUTOINCREMENT, Turso may reuse rowid values after rows are deleted. With AUTOINCREMENT, the rowid is guaranteed to be greater than any rowid that has ever existed in the table, even if rows have been deleted.
AUTOINCREMENT adds minor overhead because it updates the sqlite_sequence table on every insert. Only use it when you require strictly monotonically increasing identifiers.

NOT NULL

Prevents the column from containing NULL values. Any INSERT or UPDATE that would set the column to NULL raises a constraint error.
CREATE TABLE products (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price REAL NOT NULL
);

UNIQUE

Ensures that all values in the column are distinct. NULL values are considered distinct from each other (multiple NULL values are allowed in a UNIQUE column).
CREATE TABLE accounts (
    id INTEGER PRIMARY KEY,
    email TEXT UNIQUE
);

DEFAULT

Specifies a default value for the column when an INSERT statement does not provide one.
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    status TEXT DEFAULT 'pending',
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    quantity INTEGER DEFAULT 1
);
The default expression can be:
ExpressionExample
A literal valueDEFAULT 42, DEFAULT 'active'
NULLDEFAULT NULL
CURRENT_TIMECurrent time as HH:MM:SS
CURRENT_DATECurrent date as YYYY-MM-DD
CURRENT_TIMESTAMPCurrent datetime as YYYY-MM-DD HH:MM:SS
A parenthesized expressionDEFAULT (1 + 1)

CHECK

Defines a boolean expression that must evaluate to true (or NULL) for every row in the table. The expression can reference any column in the same row.
CREATE TABLE employees (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    age INTEGER CHECK (age >= 18),
    salary REAL CHECK (salary > 0)
);

REFERENCES (Foreign Key)

Establishes a foreign key relationship between a column and a column in another table. See Foreign Key Constraints for the full syntax.
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    customer_id INTEGER REFERENCES customers(id)
);

COLLATE

Specifies the collation sequence used for text comparisons and sorting on the column.
CollationDescription
BINARYCompares text using the raw byte values (default)
NOCASECase-insensitive comparison for ASCII characters
RTRIMLike BINARY, but ignores trailing spaces
CREATE TABLE contacts (
    id INTEGER PRIMARY KEY,
    name TEXT COLLATE NOCASE,
    code TEXT COLLATE RTRIM
);

Table Constraints

Table constraints apply to one or more columns and are specified after all column definitions.

Composite PRIMARY KEY

Defines a primary key spanning multiple columns. Each combination of values across the key columns must be unique.
CREATE TABLE enrollment (
    student_id INTEGER,
    course_id INTEGER,
    semester TEXT,
    grade REAL,
    PRIMARY KEY (student_id, course_id, semester)
);

Composite UNIQUE

Ensures that each combination of values across the specified columns is unique.
CREATE TABLE assignments (
    employee_id INTEGER,
    project_id INTEGER,
    role TEXT,
    UNIQUE (employee_id, project_id)
);

Table-Level CHECK

A CHECK constraint at the table level can reference multiple columns.
CREATE TABLE reservations (
    id INTEGER PRIMARY KEY,
    start_date TEXT NOT NULL,
    end_date TEXT NOT NULL,
    CHECK (end_date > start_date)
);

Foreign Key Constraints

Foreign key constraints enforce referential integrity between tables. They ensure that values in the child table correspond to existing values in the parent table.
Foreign key enforcement is off by default. Enable it with PRAGMA foreign_keys = ON; before performing operations that should be checked.

Syntax

-- Column constraint form (single column)
column-name type REFERENCES parent-table(parent-column)
    [ON DELETE action] [ON UPDATE action]
    [DEFERRABLE INITIALLY DEFERRED]

-- Table constraint form (single or multiple columns)
FOREIGN KEY (column-name [, column-name ...])
    REFERENCES parent-table(parent-column [, parent-column ...])
    [ON DELETE action] [ON UPDATE action]
    [DEFERRABLE INITIALLY DEFERRED]

Foreign Key Actions

The ON DELETE and ON UPDATE clauses specify what happens to child rows when a referenced parent row is deleted or updated.
ActionDescription
NO ACTIONRaises an error if the foreign key constraint is violated (default)
RESTRICTSame as NO ACTION, but checked immediately rather than at statement end
CASCADEDeletes or updates all child rows that reference the parent row
SET NULLSets the foreign key columns in child rows to NULL
SET DEFAULTSets the foreign key columns in child rows to their default values
CREATE TABLE departments (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE employees (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    dept_id INTEGER,
    manager_id INTEGER,
    FOREIGN KEY (dept_id) REFERENCES departments(id) ON DELETE CASCADE,
    FOREIGN KEY (manager_id) REFERENCES employees(id) ON DELETE SET NULL
);

STRICT Tables

STRICT tables enforce type checking at the storage layer. Every value inserted into a STRICT column must match the declared column type or be losslessly convertible to that type.
CREATE TABLE sensor_data (
    id INTEGER PRIMARY KEY,
    reading REAL NOT NULL,
    label TEXT,
    payload BLOB
) STRICT;

Allowed Column Types

STRICT tables only allow these base column types:
TypeDescription
INTEGERSigned integer
REALFloating-point number
TEXTUTF-8 string
BLOBRaw binary data
ANYAny storage class (disables type checking for this column)
Every column in a STRICT table must have an explicit type declaration.
-- This fails: STRICT tables require explicit types on every column
CREATE TABLE bad (
    id INTEGER PRIMARY KEY,
    data  -- error: missing type
) STRICT;
Turso Extension: STRICT tables also support custom types defined with CREATE TYPE. Custom types extend the type system with user-defined encoding, decoding, validation, and operator overloading. See Data Types for details.
CREATE TABLE events (
    id uuid PRIMARY KEY,
    name varchar(100) NOT NULL,
    event_date date,
    is_active boolean DEFAULT 1
) STRICT;

Unsupported Features

The following CREATE TABLE features are not yet supported:
FeatureNotes
CREATE TEMPORARY TABLETemporary tables are not supported
CREATE TABLE ... AS SELECTCreating a table from a query result is not supported
WITHOUT ROWIDWITHOUT ROWID tables are not supported
GENERATED ALWAYS ASGenerated (computed) columns are not supported
ON CONFLICT clause on column constraintsThe column-level ON CONFLICT clause (e.g., NOT NULL ON CONFLICT REPLACE) is not supported. Use the INSERT … ON CONFLICT syntax instead

Examples

Basic Table with Constraints

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    email TEXT NOT NULL UNIQUE,
    display_name TEXT,
    bio TEXT DEFAULT '',
    created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

Table with Foreign Keys and CHECK Constraints

CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    total_amount REAL NOT NULL CHECK (total_amount >= 0),
    status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'shipped', 'delivered', 'cancelled')),
    order_date TEXT NOT NULL DEFAULT CURRENT_DATE,
    FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE RESTRICT
);

STRICT Table

CREATE TABLE inventory (
    product_id INTEGER PRIMARY KEY,
    sku TEXT NOT NULL UNIQUE,
    quantity INTEGER NOT NULL DEFAULT 0 CHECK (quantity >= 0),
    price REAL NOT NULL CHECK (price > 0),
    category TEXT NOT NULL
) STRICT;

-- This succeeds: '42' is losslessly convertible to INTEGER
INSERT INTO inventory VALUES (1, 'WIDGET-001', '42', 9.99, 'widgets');

-- This fails: 'abc' cannot be converted to INTEGER
INSERT INTO inventory VALUES (2, 'GADGET-001', 'abc', 19.99, 'gadgets');
-- Error: cannot store TEXT value in INTEGER column

Composite Primary Key with Foreign Keys

CREATE TABLE course_enrollment (
    student_id INTEGER NOT NULL,
    course_id INTEGER NOT NULL,
    enrolled_date TEXT DEFAULT CURRENT_DATE,
    grade REAL CHECK (grade >= 0.0 AND grade <= 4.0),
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
    FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE
);

IF NOT EXISTS

-- Safe to run multiple times
CREATE TABLE IF NOT EXISTS sessions (
    id INTEGER PRIMARY KEY,
    user_id INTEGER NOT NULL,
    token TEXT NOT NULL UNIQUE,
    expires_at TEXT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

See Also