Неподдерживаемые фичи
Некоторым фичам SQL-стандарта уже больше 30 лет, а они до сих пор не реализованы (и, вероятно, никогда не будут).
Например, стандарт разрешает накладывать условия на таблицу целиком, но СУБД это не поддерживают.
Есть таблица сотрудников:
Накладываем условие «зарплатный фонд должен быть меньше 3000»:
И получаем:
🤷♀️
Некоторым фичам SQL-стандарта уже больше 30 лет, а они до сих пор не реализованы (и, вероятно, никогда не будут).
Например, стандарт разрешает накладывать условия на таблицу целиком, но СУБД это не поддерживают.
Есть таблица сотрудников:
create table employees (
id integer primary key,
name varchar(50),
salary integer
);
Накладываем условие «зарплатный фонд должен быть меньше 3000»:
create assertion salary_fund
check (
3000 > (select sum(salary) from employees)
);
И получаем:
error: CREATE ASSERTION is not yet implemented
🤷♀️
Кодирование данных в SQLite
Все знают про шестнадцатеричное кодирование (hex):
Но есть и другие способы.
Base32:
Base64:
Base85:
URL-кодирование:
И все они теперь доступны в SQLite с помощью расширения crypto.
Все знают про шестнадцатеричное кодирование (hex):
select hex('hello');
-- 68656C6C6F
select unhex('68656C6C6F');
-- hello
Но есть и другие способы.
Base32:
select encode('hello', 'base32');
-- NBSWY3DP
select decode('NBSWY3DP', 'base32');
-- hello
Base64:
select encode('hello', 'base64');
-- aGVsbG8=
select decode('aGVsbG8=', 'base64');
-- hello
Base85:
select encode('hello', 'base85');
-- BOu!rDZ
select decode('BOu!rDZ', 'base85');
-- hello
URL-кодирование:
select encode('hel lo!', 'url');
-- hel%20lo%21
select decode('hel%20lo%21', 'url');
-- hel lo!
И все они теперь доступны в SQLite с помощью расширения crypto.
SQL-рецепт: Сравнение с соседями
Предположим, мы хотим сравнить каждую запись с соседними. Например, сравнить продажи за месяц с предыдущим месяцем или с аналогичным месяцем в прошлом году.
Решение — использовать функцию
Пример
Сравним расходы компании от месяца к месяцу в абсолютном выражении:
По умолчанию
Нюансы
— Еще есть функция
— Если СУБД не поддерживает инструкцию
→ Оконные функции SQL
Предположим, мы хотим сравнить каждую запись с соседними. Например, сравнить продажи за месяц с предыдущим месяцем или с аналогичным месяцем в прошлом году.
Решение — использовать функцию
lag()
поверх SQL-окна.Пример
Сравним расходы компании от месяца к месяцу в абсолютном выражении:
select
year, month,
expense,
expense - lag(expense) over w as diff
from expenses
window w as (
order by year, month
)
order by year, month;
year mon exp dif
2020 1 82
2020 2 75 -7
2020 3 104 29
2020 4 94 -10
2020 5 99 5
2020 6 105 6
2020 7 95 -10
2020 8 110 15
2020 9 104 -6
lag(value, offset)
возвращает значение value
из строки, отстоящей на offset
строк назад от текущей. По умолчанию
offset
равно 1, указывать его не обязательно.Нюансы
— Еще есть функция
lead()
. Работает в точности как lag()
, только смотрит вперед, а не назад.— Если СУБД не поддерживает инструкцию
window
, можно встроить определение окна: lag(expense) over (order by ...)
→ Оконные функции SQL
25 текстовых функций в SQLite
С помощью стандартных функций instr, substr и replace можно много чего сделать. Но мне хотелось более полного набора возможностей, как в PostgreSQL, Python или Go. Пришлось добавить.
Новое расширение text содержит 25 функций для работы со строками: от slice, contains и count до split_part, trim и repeat. И многие из них совместимы с PostgreSQL!
А если добавить функции работы с регулярками из regexp, вас и вовсе будет не остановить ツ
С помощью стандартных функций instr, substr и replace можно много чего сделать. Но мне хотелось более полного набора возможностей, как в PostgreSQL, Python или Go. Пришлось добавить.
Новое расширение text содержит 25 функций для работы со строками: от slice, contains и count до split_part, trim и repeat. И многие из них совместимы с PostgreSQL!
А если добавить функции работы с регулярками из regexp, вас и вовсе будет не остановить ツ
Sqlean shell
И теперь он доступен в комплекте с SQLite CLI!
https://github.com/nalgeon/sqlite/releases/3.42.0-sqlean
sqlean
— это набор базовых расширений SQLite, от регулярных выражений и мат. статистики до работы с файлами и динамического SQL.И теперь он доступен в комплекте с SQLite CLI!
https://github.com/nalgeon/sqlite/releases/3.42.0-sqlean
Покрывающий индекс в SQL
Покрывающий индекс — самый быстрый способ выбрать данные из таблицы.
Например, есть запрос, который выбирает сотрудников с указанной зарплатой:
Если нет индекса, он обходит всю таблицу (так называемый фулскан).
Создадим индекс по зарплате:
Теперь тот же запрос будет находить записи по зарплате в индексе (это быстрее, чем обходить всю таблицу). И для каждой найденной записи будет обращаться к таблице, чтобы получить значения id и name.
Но если создать покрывающий индекс:
То запрос будет отрабатывать только по индексу, вообще без обращения к таблице. Это еще быстрее.
Покрывающие индексы дорого обходятся при изменении данных в таблице, поэтому на каждый вид запросов их создавать не стоит. Чаще это одно из последних средств оптимизации, когда все остальное уже сделали.
Покрывающий индекс — самый быстрый способ выбрать данные из таблицы.
Например, есть запрос, который выбирает сотрудников с указанной зарплатой:
select id, name
from employees
where salary = 90;
Если нет индекса, он обходит всю таблицу (так называемый фулскан).
Создадим индекс по зарплате:
create index employees_idx
on employees(salary);
Теперь тот же запрос будет находить записи по зарплате в индексе (это быстрее, чем обходить всю таблицу). И для каждой найденной записи будет обращаться к таблице, чтобы получить значения id и name.
Но если создать покрывающий индекс:
create index employees_idx
on employees(salary, id, name);
То запрос будет отрабатывать только по индексу, вообще без обращения к таблице. Это еще быстрее.
Покрывающие индексы дорого обходятся при изменении данных в таблице, поэтому на каждый вид запросов их создавать не стоит. Чаще это одно из последних средств оптимизации, когда все остальное уже сделали.
sqlite3 + расширения на Python
Сделал Python-пакет
— кодирование/декодирование
— динамический SQL
— работа с файлами
— текстовые функции
— IP адреса
— мат. статистика
— UUID
— CSV
https://github.com/nalgeon/sqlean.py
Сделал Python-пакет
sqlean.py
. Он полностью совместим со стандартным sqlite3
, плюс добавляет множество приятных функций:— кодирование/декодирование
— динамический SQL
— работа с файлами
— текстовые функции
— IP адреса
— мат. статистика
— UUID
— CSV
https://github.com/nalgeon/sqlean.py
Больше функций в SQL-песочнице
Sqlime — это опенсорсная SQLite-песочница в браузере.
В ней всегда были стандартные функции SQLite: общего назначения, дата-время и математические.
А теперь есть намного больше, от регулярных выражений и матстатистики до динамического SQL!
По сути, я приделал к песочнице все свои расширения, которые до этого подключались только к «обычному» SQLite (кроме работы с файлами и юникода, который под веб не захотел компилироваться).
https://sqlime.org/
Sqlime — это опенсорсная SQLite-песочница в браузере.
В ней всегда были стандартные функции SQLite: общего назначения, дата-время и математические.
А теперь есть намного больше, от регулярных выражений и матстатистики до динамического SQL!
По сути, я приделал к песочнице все свои расширения, которые до этого подключались только к «обычному» SQLite (кроме работы с файлами и юникода, который под веб не захотел компилироваться).
https://sqlime.org/
Реестр и менеджер пакетов для SQLite
Расширений для SQLite становится все больше, и я решил, что пришло время сделать менеджер пакетов!
Встречайте sqlpkg - это реестр пакетов с веб-интерфейсом и одноименный консольный менеджер пакетов. Надеюсь, вам пригодится.
https://sqlpkg.org/
Реестр пакетов работает отдельно от менеджера и не требует его. Достаточно найти нужный пакет на сайте, скачать под свою ОС и загрузить стандартными средствами (.load или load_extension).
А менеджер пакетов работает отдельно от реестра и тоже не требует его :) Чтобы установить пакет, достаточно указать менеджеру на файл спецификации. Его можно захостить где угодно или даже разместить локально.
Расширений для SQLite становится все больше, и я решил, что пришло время сделать менеджер пакетов!
Встречайте sqlpkg - это реестр пакетов с веб-интерфейсом и одноименный консольный менеджер пакетов. Надеюсь, вам пригодится.
https://sqlpkg.org/
Реестр пакетов работает отдельно от менеджера и не требует его. Достаточно найти нужный пакет на сайте, скачать под свою ОС и загрузить стандартными средствами (.load или load_extension).
А менеджер пакетов работает отдельно от реестра и тоже не требует его :) Чтобы установить пакет, достаточно указать менеджеру на файл спецификации. Его можно захостить где угодно или даже разместить локально.
Одна из моих любимых мелких штук в Sqlime — возможность выделить и запустить фрагмент запроса. Полезно, когда «распутываешь» сложный запрос.
К тому же, ее было несложно реализовать. Ах, если бы все фичи были такими 🙂
К тому же, ее было несложно реализовать. Ах, если бы все фичи были такими 🙂
SQL-файлы в песочнице
Когда я запустил Sqlime, как-то забыл поддержать загрузку из
Наверно я полагал, что люди в основном будут копипастить SQL руками, так что файлы им не нужны. Возможно, не так уж и ошибался — никто не попросил эту возможность.
Но все же странновато для SQL-песочницы не уметь загружать SQL-файлы. Так что теперь она умеет ツ
Заодно обновил иконки. Использовал svgrepo.com — опенсорсный репозиторий иконок, очень рекомендую.
Когда я запустил Sqlime, как-то забыл поддержать загрузку из
.sql
файлов. Песочница умела загружать бинарные SQLite-базы из локального файла или по урлу, GitHub-гисты, но не sql-файлы.Наверно я полагал, что люди в основном будут копипастить SQL руками, так что файлы им не нужны. Возможно, не так уж и ошибался — никто не попросил эту возможность.
Но все же странновато для SQL-песочницы не уметь загружать SQL-файлы. Так что теперь она умеет ツ
Заодно обновил иконки. Использовал svgrepo.com — опенсорсный репозиторий иконок, очень рекомендую.
Перенести SQLite-расширения с одной машины на другую
Начиная с релиза 0.1.0,
Используйте его, чтобы установить расширения на новой машине одной командой:
https://github.com/nalgeon/sqlpkg-cli/releases/0.1.0
Начиная с релиза 0.1.0,
sqlpkg
сохраняет информацию об установленных пакетах в специальном файле (sqlpkg.lock
, он же локфайл). Используйте его, чтобы установить расширения на новой машине одной командой:
sqlpkg install
https://github.com/nalgeon/sqlpkg-cli/releases/0.1.0
Побег из карантина macOS и точный путь к расширению
Свежий релиз
— Команда
— Команда
https://github.com/nalgeon/sqlpkg-cli/releases/0.2.0
Свежий релиз
sqlpkg
принес две приятные фичи:— Команда
install
автоматически убирает расширение из карантина macOS, чтобы можно было использовать без дополнительных приседаний.— Команда
which
печатает точный путь к установленному расширению, чтобы было проще загрузить его в SQLite.https://github.com/nalgeon/sqlpkg-cli/releases/0.2.0
SQL-полиглот
Сделал онлайн-утилиту, которая выполняет заданный запрос на любой СУБД от PostgreSQL до DuckDB (SQLite тоже есть, конечно).
Удобно, чтобы быстро понять, поддерживает ли база тот или иной SQL-синтаксис.
https://codapi.org/sql
Сделал онлайн-утилиту, которая выполняет заданный запрос на любой СУБД от PostgreSQL до DuckDB (SQLite тоже есть, конечно).
Удобно, чтобы быстро понять, поддерживает ли база тот или иной SQL-синтаксис.
https://codapi.org/sql
codapi.org
SQL Polyglot
Try running a query anywhere from PostgreSQL to DuckDB in your browser.
Современный SQLite: STRICT-таблицы
Я начинаю марафон! Но не марафон желаний 😅 Буду вкратце рассказывать о полезных функциях современного SQLite, про которые вы (возможно) не слышали.
Начнем со «строгих» таблиц.
Как вы наверняка знаете, SQLite обладает гибкой системой типов (за что некоторые даже называют его «джаваскриптом в мире СУБД»). Вы можете хранить любые значения в столбцах любых типов: например, строки в INTEGER-столбце или бинарные данные в REAL-столбце.
Кто-то любит SQLite за эту гибкость, другие ненавидят за нее же. Поэтому в какой-то момент авторы SQLite добавили «строгие» (STRICT) таблицы:
Они проверяют типы так же, как традиционные СУБД вроде PostgreSQL или MySQL:
Даже в строгой таблице можно объявить столбец как ANY — тогда в нем можно хранить значения любых типов. Получается, можно взять лучшее от обоих миров — строгую проверку типов и опциональную гибкую типизацию.
Работает в SQLite 3.37+ (ноябрь 2021).
песочница • документация
Я начинаю марафон! Но не марафон желаний 😅 Буду вкратце рассказывать о полезных функциях современного SQLite, про которые вы (возможно) не слышали.
Начнем со «строгих» таблиц.
Как вы наверняка знаете, SQLite обладает гибкой системой типов (за что некоторые даже называют его «джаваскриптом в мире СУБД»). Вы можете хранить любые значения в столбцах любых типов: например, строки в INTEGER-столбце или бинарные данные в REAL-столбце.
Кто-то любит SQLite за эту гибкость, другие ненавидят за нее же. Поэтому в какой-то момент авторы SQLite добавили «строгие» (STRICT) таблицы:
create table people (
id integer primary key,
name text,
salary real
) strict;
Они проверяют типы так же, как традиционные СУБД вроде PostgreSQL или MySQL:
insert into people (name, salary)
values ('Alice', 100);
-- OK
insert into people (name, salary)
values ('Bob', 90.5);
-- OK
insert into people (name, salary)
values ('Cindy', 'decent');
-- Runtime error: cannot store TEXT value
-- in REAL column people.salary
Даже в строгой таблице можно объявить столбец как ANY — тогда в нем можно хранить значения любых типов. Получается, можно взять лучшее от обоих миров — строгую проверку типов и опциональную гибкую типизацию.
Работает в SQLite 3.37+ (ноябрь 2021).
песочница • документация
Telegram
SQLite на практике
Гибкие типы данных
Люди часто не понимают, что за ерунда происходит в SQLite с типами данных. Давайте разберемся.
SQLite использует пять типов данных:
— INTEGER — целые числа,
— REAL — действительные числа,
— TEXT — строки,
— BLOB — бинарные данные,
—…
Люди часто не понимают, что за ерунда происходит в SQLite с типами данных. Давайте разберемся.
SQLite использует пять типов данных:
— INTEGER — целые числа,
— REAL — действительные числа,
— TEXT — строки,
— BLOB — бинарные данные,
—…
Современный SQLite: Вычисляемые столбцы
Вычисляемые (generated) столбцы рассчитываются на основании других столбцов той же таблицы. Например, мы можем рассчитать процент отказов на основе количества запросов:
Другой распространенный сценарий — вытащить поле JSON-документа в отдельный столбец, и при необходимости проиндексировать его:
Результат:
Так SQLite превращается в документную базу данных:
— Храним JSON-документы как текст или blob-поле.
— Извлекаем отдельные поля через вычисляемые столбцы.
— Строим индексы, если нужна быстрая выборка.
Вычисляемые столбцы могут рассчитываться на лету (virtual) или храниться на диске (stored). Stored-столбцы редко используют на практике.
Работает в SQLite 3.31+ (январь 2020).
песочница • документация
Вычисляемые (generated) столбцы рассчитываются на основании других столбцов той же таблицы. Например, мы можем рассчитать процент отказов на основе количества запросов:
create table stats (
date text,
n_total int,
n_failed int,
fail_perc as (n_failed*100.0 / n_total)
);
Другой распространенный сценарий — вытащить поле JSON-документа в отдельный столбец, и при необходимости проиндексировать его:
create table events (
id integer primary key,
event blob,
etime text as (event ->> 'time'),
etype text as (event ->> 'type')
);
create index events_time on events(etime);
insert into events(event) values
('{"time": "2024-05-01", "type": "credit"}'),
('{"time": "2024-05-02", "type": "debit"}'),
('{"time": "2024-05-03", "type": "close"}');
select etime, etype from events;
Результат:
┌────────────┬────────┐
│ etime │ etype │
├────────────┼────────┤
│ 2024-05-01 │ credit │
│ 2024-05-02 │ debit │
│ 2024-05-03 │ close │
└────────────┴────────┘
Так SQLite превращается в документную базу данных:
— Храним JSON-документы как текст или blob-поле.
— Извлекаем отдельные поля через вычисляемые столбцы.
— Строим индексы, если нужна быстрая выборка.
Вычисляемые столбцы могут рассчитываться на лету (virtual) или храниться на диске (stored). Stored-столбцы редко используют на практике.
Работает в SQLite 3.31+ (январь 2020).
песочница • документация
Работа с датой и временем в SQLite
В sqlite есть встроенные функции для работы с датами, но они мне всегда не слишком нравились.
Поэтому разработал расширение
https://antonz.org/sqlean-time
В sqlite есть встроенные функции для работы с датами, но они мне всегда не слишком нравились.
Поэтому разработал расширение
sqlean-time
. Оно поддерживает точность вплоть до наносекунд и предоставляет удобное структурированное API с большим количеством функций.https://antonz.org/sqlean-time
antonz.org
High-precision date/time in SQLite
A rich set of functions for working with date, time and durations.