Вы когда-нибудь задумывались, почему в 2026 году технические статьи про базы данных всё еще выглядят ��ак же, как в 2005? Я вижу тонны статического текста, скриншоты терминалов и блоки кода, которые читатель должен копировать и запускать где-то у себя.
В этой статье я расскажу о технической реализации SQLize Embed — легковесного JS-компонента, который превращает любую страницу в живую лабораторию SQL. Я разберу, как это работает под капотом: от фронтенда на Ace Editor до изоляции выполнения запросов на бэкенде.
Проблема: «Смерть» примера кода
Типичный путь читателя туториала:
Видит интересный запрос
SELECT ... OVER (PARTITION BY ...).Думает: «Круто, надо попробовать на своих данных».
Вспоминает, что у него стоит PostgreSQL 14, а пример для 17-й версии.
Открывает Docker, ищет образ, ждет загрузки...
В 50% случаев на этом этапе интерес пропадает.
Я решил сократить это расстояние до одного клика (кнопки "Run SQL").
Архитектура решения
Система состоит из трех уровней:
Frontend (Embed SDK): JS-библиотека, которая инициализирует редакторы и управляет состоянием UI.
Execution API: Прослойка, отвечающая за квоты, кэширование сессий и безопасность.
Backend-кластер: Ферма изолированных контейнеров с различными версиями СУБД (MySQL, PostgreSQL, Oracle, MS SQL, и т.д.).
Фронтенд: Ace Editor и MutationObserver
Я выбрал Ace Editor за его производительность и гибкость. Скрипт sqlize-embed.js весит совсем немного, так как подгружает тяжелые части редактора с CDN только при наличии на странице элементов [data-sqlize-editor].
Интересный момент — работа с динамическим контентом. Если ваш сайт подгружает статьи через AJAX или использует бесконечный скролл, обычные методы инициализации не сработают. Я применил MutationObserver:
new MutationObserver((mutations) => {
let shouldInit = false;
mutations.forEach(m => {
m.addedNodes.forEach(node => {
if (node.nodeType === 1 && (node.hasAttribute('data-sqlize-editor'))) {
shouldInit = true;
}
});
});
if (shouldInit) initSQLize();
}).observe(document.body, { childList: true, subtree: true });
Это позволяет «подхватывать» новые редакторы сразу после их появления в DOM.
Процесс выполнения запроса
Когда пользователь нажимает «Run», происходит двухфазный процесс:
Phase 1: Session Hashing. Я отправляю код и версию СУБД на эндпоинт
/hash.php. Сервер генерирует уникальный идентификатор сессии. Это позволяет мне не гонять огромные куски SQL кода в URL и обеспечивает базу для кэширования результатов.Phase 2: Execution & Streaming. Клиент обращается к
/sqleval.php?sqlses={hash}. Сервер находит соответствующую задачу, отправляет её в нужный контейнер и возвращает результат в виде отформатированного HTML (или JSON, в зависимости от настроек).
Технические подробности бэкенда
Самое сложное — это поддержка огромного количества версий. На текущий момент SQLize поддерживает:
MySQL: 8.0, 9.3.0
PostgreSQL: от 14 до 18 (включая PostGIS)
MS SQL Server: от 2017 до новейшей 2025
Oracle: 19c, 21c, 23ai
MariaDB, SQLite, Firebird, ClickHouse и даже экзотику вроде SOQOL.
Внутри это работает на Docker-контейнерах с жесткими лимитами по CPU/Memory и read-only файловой системой для пользовательских запросов (если не подразумевается CREATE TABLE).
Безопасность и изоляция
При встраивании кода в чужие сайты встает вопрос безопасности (CORS, XSS). Я использую строгие политики CORS: выполнение запросов разрешено только для доменов, имеющих активную подписку. Это предотвращает нецелевое использование моих вычислительных мощностей.
Как внедрить?
Внедрение требует минимум усилий.
Подключаем SDK:
<script src="https://sqlize.online/js/sqlize-embed.js"></script>
Создаем контейнер:
<div data-sqlize-editor
data-sql-version="psql17"
code-rows="10">
-- Пример использования оконных функций в PostgreSQL 17
SELECT
name,
salary,
AVG(salary) OVER(ORDER BY salary ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) as moving_avg
FROM (VALUES ('Alice', 5000), ('Bob', 6000), ('Charlie', 7000)) AS employees(name, salary);
</div>
Атрибуты настройки:
data-sql-version: идентификатор движка (например,mysql93,mssql2025).data-read-only: еслиtrue, пользователь увидит код, сможет его запустить, но не сможет изменить.code-rows/result-rows: фиксированная высота блоков (в строках).
Цепочки запросов (Chaining): наследование контекста
Одной из самых интересных функций стала возможность связывать несколько редакторов в цепочку. Представьте, что в начале статьи вы создаете схему базы данных, а в последующих разделах постепенно наполняете её данными и выполняете сложные запросы.
Чтобы не заставлять читателя видеть (и копировать) один и тот же DDL-код в каждом блоке, я реализовал поддержку атрибутов data-sqlize-id и data-sqlize-parent. Это работает как цепочка наследования: при нажатии кнопки «Run» скрипт рекурсивно обхо��ит дерево родителей и собирает весь код воедино перед отправкой на сервер.
Это позволяет сохранять логический контекст обучения на протяжении всей лонгрид-статьи, не перегружая визуальное пространство лишним кодом.
Что дальше?
Я активно работаю над поддержкой векторных типов данных (уже доступны в MariaDB 11.8 через мой SDK) и интеграцией с LLM для автоматического исправления ошибок в запросах прямо внутри встраиваемого редактора.
Если вы пишете про базы данных — попробуйте сделать свои статьи интерактивными. Это кардинально меняет вовлеченность аудитории.
Буду рад ответить на вопросы в комментариях! Какие СУБД вам было бы интересно увидеть в песочнице?
