Menu

SQLite CREATE TABLE: синтаксис, ограничения, примеры

Разбираем CREATE TABLE в SQLite: описание колонок, ограничения, IF NOT EXISTS, временные таблицы и CREATE TABLE AS SELECT с примерами.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Создание таблицы через CREATE TABLE

В SQLite любые структурированные данные хранятся в таблицах, и каждая таблица начинается с инструкции CREATE TABLE. Вы задаёте имя, перечисляете колонки и при желании добавляете ограничения. SQLite сохраняет схему прямо в файл базы данных — и таблицей уже можно пользоваться.

Минимальный рабочий пример создания таблицы SQL:

Три столбца, один первичный ключ, одно правило NOT NULL. SQLite сам подставил id, потому что это целочисленный primary key, а во второй строке оставил email пустым (NULL) — мы ведь нигде не запретили. Вот и вся суть: имя таблицы, столбцы, ограничения. Всё остальное на этой странице — лишь вариации на ту же тему.

Разбираем синтаксис по частям

Описание столбца строится по схеме имя ТИП ограничение ограничение .... В классическом SQLite тип указывать необязательно (подробнее — на странице про type affinity), но лучше всё же его писать всегда: и людям, и инструментам так понятнее.

Несколько моментов, на которые стоит обратить внимание:

  • Ограничения пишутся через пробел: NOT NULL UNIQUE у поля sku означает, что действуют оба правила сразу.
  • DEFAULT 1 для in_stock позволяет не указывать эту колонку при INSERT.
  • В SQLite булевы значения хранятся в INTEGER — отдельного типа BOOLEAN тут нет. 0 — это false, 1true.
  • Запятая после последней колонки — синтаксическая ошибка. SQL строже, чем JavaScript.

CREATE TABLE IF NOT EXISTS: чтобы скрипт не падал при повторном запуске

Если выполнить CREATE TABLE для базы, где такая таблица уже есть, SQLite выдаст ошибку:

Ошибка: таблица users уже существует

В первый раз это нормально, но к сотому разу начинает раздражать. IF NOT EXISTS превращает запрос в no-op, если таблица уже существует:

Второй CREATE TABLE просто ничего не делает — ни ошибки, ни изменений в схеме. Именно такая форма нужна в стартовом коде, миграционных скриптах и везде, где один и тот же SQL может выполниться не один раз.

Но есть нюанс: IF NOT EXISTS сверяет только имя. Если таблица с таким именем уже есть, но колонки в ней другие, SQLite оставит её как есть. Он не станет «чинить» или «обновлять» схему за вас — для этого существуют миграции.

Ограничения: правила, которые живут вместе со схемой

Ограничения (constraints) — это способ перенести валидацию данных на уровень самой базы. Вот четвёрка, к которой вы будете обращаться постоянно:

  • PRIMARY KEY — однозначно идентифицирует строку. Подробнее об этом — в отдельной статье про первичные ключи.
  • NOT NULL — колонка обязана содержать значение.
  • DEFAULT значение — подставляется, когда INSERT пропускает колонку. Можно указать литерал или выражение, например datetime('now').
  • CHECK (выражение) — должно быть истинным для каждой строки.
  • UNIQUE (col, col) — ограничение уровня таблицы, гарантирующее уникальность комбинации значений.

Ограничения срабатывают при каждом INSERT и UPDATE. Строка, нарушающая хотя бы одно из них, отклоняется, и весь запрос падает с ошибкой. Поймать кривые данные на уровне БД куда дешевле, чем разгребать их потом, когда они уже расползлись по приложению.

Внешние ключи (foreign key) в SQLite

Внешний ключ говорит: «эта колонка ссылается на строку в другой таблице». Он держит данные в согласованном состоянии — вы не сможете сослаться на несуществующего пользователя, а при правильных настройках удаление пользователя каскадно подчистит и его заказы.

SQLite-нюанс, который стоит запомнить раз и навсегда: проверка внешних ключей по умолчанию выключена. Чтобы ограничения действительно работали, нужно выполнить PRAGMA foreign_keys = ON для каждого нового соединения. Большинство драйверов делают это сами или дают соответствующую настройку; если ваш — нет, выполните прагму сразу после подключения.

ON DELETE CASCADE в этом примере означает, что при удалении пользователя автоматически удалятся и все его посты. Есть и другие варианты: SET NULL, RESTRICT, а также поведение по умолчанию — NO ACTION, которое запрещает удаление, если у записи есть дочерние строки.

CREATE TABLE AS SELECT в SQLite

Иногда хочется быстро сохранить результат запроса как отдельную таблицу — сделать снапшот, бэкап или временную таблицу для анализа. С этим отлично справляется CREATE TABLE ... AS SELECT:

В новую таблицу попадут имена колонок, типы (по мере возможности) и сами данные. А вот чего туда не попадёт — не менее важно: ни первичного ключа, ни NOT NULL, ни индексов, ни внешних ключей. Это просто плоский слепок. Используйте его как отправную точку для разовых задач, а не как способ клонировать настоящую схему.

Если нужна только структура без данных, допишите WHERE 0:

Получаешь пустую таблицу с той же структурой колонок — удобно для архивных таблиц, которые ты заполнишь позже.

Временные таблицы в SQLite

Таблица TEMP существует только в рамках текущего подключения к базе. Закрыл соединение — таблицы как не бывало: ни чистить ничего не надо, ни схема нигде не остаётся:

Хорошие сценарии: подготовить строки для многошагового запроса, сохранить промежуточные результаты, которые слишком громоздки для CTE, или изолировать данные конкретного соединения в долгой сессии. CREATE TEMP TABLE и CREATE TEMPORARY TABLE — это одно и то же.

Можно связать с AS SELECT: конструкция CREATE TEMP TABLE snapshot AS SELECT ... часто применяется, чтобы зафиксировать срез данных в середине анализа.

Экранирование имён

В большинстве случаев имена таблиц и колонок — это обычные идентификаторы без кавычек. Но если приходится использовать зарезервированное слово или имя с пробелом, оберните его в двойные кавычки (так велит стандарт SQL) либо в обратные кавычки (наследие MySQL, которое SQLite тоже понимает):

Работает, конечно, но каждый раз возиться с кавычками при обращении к таблице — то ещё удовольствие. Лучше сразу давать обычные имена: orders, selection, user_id — и забыть про кавычки как страшный сон.

Живой пример: create table sqlite на практике

Соберём всё в кучу — небольшая схема для приложения задач, с IF NOT EXISTS, чтобы её можно было гонять при каждом запуске:

Такую схему уже не стыдно отправить в прод: повторный запуск ничего не сломает, внешние ключи реально работают, CHECK следит за тем, чтобы done хранил адекватные значения, значения по умолчанию расставлены, а временные метки заполняются сами.

Дальше: типы данных колонок

В CREATE TABLE можно написать INTEGER, TEXT, REAL — но SQLite, как известно, относится к типам колонок весьма вольно. На следующей странице разберём пять классов хранения, которыми SQLite пользуется на самом деле, и поймём, почему тип, который вы указали, далеко не всегда совпадает с тем, что окажется в базе.

Часто задаваемые вопросы

Как создать таблицу в SQLite?

Базовый синтаксис — CREATE TABLE имя (колонка1 ТИП, колонка2 ТИП, ...). У каждой колонки есть имя и (необязательно) тип, а к ней можно прицепить ограничения: PRIMARY KEY, NOT NULL, DEFAULT и другие. Запрос выполняется сразу же, и таблица сохраняется в файле базы данных.

Зачем нужен IF NOT EXISTS в CREATE TABLE?

Конструкция CREATE TABLE IF NOT EXISTS имя (...) создаёт таблицу только в том случае, если её ещё нет. Без этой проверки повторный запуск скрипта на уже существующей базе упадёт с ошибкой table already exists. На практике это стандартная страховка для миграций и кода, который выполняется при старте приложения.

Можно ли создать таблицу из SELECT в SQLite?

Да, для этого есть CREATE TABLE new_name AS SELECT ... — новая таблица собирается прямо из результата запроса. Но учтите: копируются только имена колонок и сами данные, а вот ограничения, первичные ключи и индексы из исходной таблицы не переносятся. Такой приём удобен для снапшотов и временных служебных таблиц, но полноценную схему так не построить.

Чем временная таблица отличается от обычной?

CREATE TEMP TABLE (он же CREATE TEMPORARY TABLE) создаёт таблицу, которая живёт только в рамках текущего соединения и исчезает, как только вы его закроете. Обычные таблицы хранятся в файле базы постоянно. Временные таблицы удобны, когда нужно отложить промежуточные результаты запроса, не засоряя основную схему.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ