Домой Edit me on GitHub

2019-06-19

Каналы передачи данных | Сетевое программирование | Базы данных | Основы Веб-программирования

SQLite

SQLite — компактная встраиваемая реляционная база данных. Исходный код библиотеки передан в общественное достояние. Является чисто реляционной базой данных.

Слово «встраиваемый» означает, что SQLite не использует парадигму клиент-сервер. Т.е. движок SQLite не является отдельно работающим процессом, с которым взаимодействует программа, а предоставляет библиотеку, с которой программа компонуется и движок становится составной частью программы. Таким образом, в качестве протокола обмена используются вызовы функций (API) библиотеки SQLite. Такой подход уменьшает накладные расходы, время отклика и упрощает программу. SQLite хранит всю базу данных (включая определения, таблицы, индексы и данные) в единственном стандартном файле на том компьютере, на котором исполняется программа. Простота реализации достигается за счёт того, что перед началом исполнения транзакции записи весь файл, хранящий базу данных, блокируется; ACID [1]-функции достигаются в том числе за счёт создания файла журнала.

Несколько процессов или потоков могут одновременно без каких-либо проблем читать данные из одной базы. Запись в базу можно осуществить только в том случае, если никаких других запросов в данный момент не обслуживается; в противном случае попытка записи оканчивается неудачей, и в программу возвращается код ошибки. Другим вариантом развития событий является автоматическое повторение попыток записи в течение заданного интервала времени. Можно, также, ввести таймаут операций. Тогда подключение, столкнувшись с занятостью БД, будет ждать N секунду прежде, чем отвалиться с ошибкой SQLITE_BUSY.

Также с версии 3.7.0 присутствует режим WAL [2], с помощью которого можно испольовать одну и ту же базу несколькими приложениями, как на чтение, так и на запись.

В комплекте поставки идёт также функциональная клиентская часть в виде исполняемого файла sqlite3, с помощью которого демонстрируется реализация функций основной библиотеки. Клиентская часть работает из командной строки, позволяет обращаться к файлу БД на основе типовых функций ОС.

Благодаря архитектуре движка возможно использовать SQLite как на встраиваемых системах, так и на выделенных машинах с гигабайтными массивами данных.

Формат файла базы данных является кросс-платформеным, что позволяет без проблем использовать одну и ту же базу на нескольких операционных системах. Также присутствует возможность хранения базы в памяти, без её записи на диск. Этот вариант используется по умолчанию для консольной утилиты sqlite3, если не указано имя файла.

Поддержка стандарта SQL

Как известно, в своем развитии SQL устремился в разные стороны. Крупные производители начали впихивать всякие расширения. И хотя принимаются всякие стандарты, в реальной жизни все крупные БД не поддерживают стандартов полностью. Но зато имеют что-то свое.

Так вот, SQLite старается жить по принципу «минимальный, но полный набор». Она не поддерживает сложные штуки, но во многом соответствует SQL 92. И вводит некие свои особенности, которые очень удобны, но — не стандартны.

Неподдерживаются следующие возможности:

  • RIGHT и FULL OUTER JOIN. Реализован только LEFT OUTER JOIN.
  • Частично реализован ALTER TABLE. Доступны только RENAME TABLE и ADD COLUMN.
  • Частичная поддержка тригеров. Доступены только FOR EACH ROW тригеры.
  • Запись во VIEWS. В SQLite VIEWS доступны только на чтение. Частично обходится через тригеры.
  • В силу реализации базы данных, как единственного файла и отхода от концепции «клиент-сервер», не используются возможности GRANT и REVOKE.
  • По умолчанию отключены внешние ключи. Это сделано для обратной совместимости.

Типизация

SQLite использует динамическое типизирование данных. Это значит, что тип столбца не определяет тип хранимого значения в этом поле записи. Т.е. в любой столбец можно занести любое значение.

Тип столбца определяет как сравнивать значения (нужно же их привести к единому типу при сравнении, скажем, внутри индекса). Но не обязывает заносить значения именно такого типа в столбец.

Допустим, мы объявили столбец как «A INTEGER». SQLite позволяет занести в этот столбец значения любого типа (999, «abc», «123», 678.525). Если вставляемое значение — не целое, то SQLite пытается привести его к целому. Т.е. строка «123» превратится в целое 123, а остальные значения запишутся «как есть».

Возможные типы полей: NULL, INTEGER, REAL, TEXT, BLOB.

Надёжность

Ситуация с покрытиями тестами исходного кода SQLite в некотором роде является легендой.

Это связно с тем, что тестами описано практически все возможные (и невозможные) ситуации. Кода, описывающего тесты, намного больше, чем кода реализующего SQLite. Естественно, что покрытие кода тестами 100%.

Сам же подход к тестированию использующийся разработчиками SQLite достоин подражания. И может быть взят как идеал, к которому следуют стремиться.

Практика

В качестве практического занятия для закрепления понимания реляционной модели и иллюстрации её ограниченности, мы рассмотрим пример создания структуры каталога товаров.

Для того чтобы запустить SQL-код необходимо выполнить следующую команду:

sqlite3 ~/example.sqlite

Далее откроется интерактивный SQLite-shell, куда можно вводить команды.

Создание структуры каталога

В этом примере мы рассмотрим как создать простую струкртуру каталога товаров.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CREATE TABLE category (name TEXT NOT NULL);

INSERT INTO category (name) VALUES
    ('Тапки'),
    ('Самолёты'),
    ('Ноутбуки');


CREATE TABLE product (
    name TEXT NOT NULL,
    price NUMERIC NOT NULL,
    category REFERENCES category(rowid)
);

INSERT INTO product (name, price, category) VALUES
    ('Босоножки', 1.17, 1),
    ('Вьетнамки', 2.36, 1),
    ('Макасины', 4.99, 1),
    ('ИЛ-2', 556000, 2),
    ('Суперджет 100', 1500000, 2),
    ('Ту-160', 25000000, 2),
    ('Dell', 590, 3),
    ('Lenovo', 200, 3),
    ('Sony', 437, 3);

Расширение структуры каталога

В этом примере мы рассмотрим, как расширить существующую структуру под новые требования.

Требования будут следующими:

  • Для категории «Тапки» необходимо добавить поле «Размер».
  • Для категории «Самолёты» необходимо добавить поле «Вместимость».
  • Для категории «Ноутбуки» необходимо добавить поле «Прцессор».
1
2
3
4
5
ALTER TABLE product ADD size INTEGER;

ALTER TABLE product ADD capacity INTEGER;

ALTER TABLE product ADD processor TEXT;

Сущетсвенным недостатком данного подхода является тот факт, что поля одних категорий товаров являются обязательными для других категорий. Т.е., например, поле «размер», которое добавлено для категории «Тапки», становится обязательным и для товаров из категории «Самолёты» и «Ноутбуки».

Классическая реляционная модель не позволяет удобно применить отдельные поля для разных категорий. Можно, конечно, использовать поле типа BLOB и складывать туда всё, что душе вздумается, но тогда потеряются такие прелести реляционных СУБД, как индексы, тригеры (либо они очень усложнятся) и фильтрация в запросах.

Можно ещё, как вариант, оставить базовые поля в одной таблице, а остальные вынести в дополнительные, связав их внешними ключами. Но это, опять же, ненужное усложнение.

Поэтому предлагаю оставить замечательную реляционную базу SQLite в покое. Она замечательно справляется с возложенными на неё обязанностями. Благодаря чему и является, пожалуй, самой популярной встраиваемой базой данных в мире.

[1]Atomicity Consistency Isolation Durability
[2]Write-Ahead Logging
Previous: Реляционная модель данных Next: ZODB