Обычно SQL используют ради отчётов, аналитики и унылого «выгрузить за вчера». Но у языка запросов есть и другая, неожиданная сторона: если относиться к нему как к инструменту для сочинительства, можно попробовать написать рассказ. Сюжет, герои, диалоги — всё это вполне собирается на голом SQL. В статье я делюсь экспериментом, который начался ради шутки, а закончился странным ощущением, что база данных умеет рассказывать истории.
SQL я впервые выучил не ради красоты — нужен был для работы. Тогда казалось: язык скучный, служебный, без «души». SELECT, WHERE, JOIN… будто молоток или отвёртка. Но однажды, копаясь в старой демо-базе, я обратил внимание на то, что данные сами по себе напоминали короткие предложения. И пришла мысль: а что, если воспринимать таблицу не как набор строк, а как страницу романа?
Сначала это выглядело как дурацкая затея, но чем дальше я шёл, тем больше SQL переставал быть «сухим инструментом» и начинал вести себя как настоящий рассказчик.

Таблицы вместо сеттинга
Любой рассказ начинается с декораций. У писателя — это сеттинг, у нас — таблица. Простейший каркас истории выглядит так:
CREATE TABLE Characters ( id INT PRIMARY KEY, name VARCHAR(50), role VARCHAR(50), mood VARCHAR(50) ); CREATE TABLE Places ( id INT PRIMARY KEY, name VARCHAR(50), description TEXT ); CREATE TABLE Events ( id INT PRIMARY KEY, place_id INT, character_id INT, action TEXT, FOREIGN KEY (place_id) REFERENCES Places(id), FOREIGN KEY (character_id) REFERENCES Characters(id) );
На этом этапе это всё ещё больше похоже на заготовку для какой-нибудь текстовой RPG, но я-то собираюсь писать «литературу».
Герои истории
Герои без базы данных не заведутся. Пусть будет программист, аналитик и ироничный ИИ.
INSERT INTO Characters (id, name, role, mood) VALUES (1, 'Алекс', 'программист', 'усталый'), (2, 'Ира', 'аналитик', 'вдохновлённая'), (3, 'Бот', 'ИИ-ассистент', 'саркастичный');
Я заметил, что когда вручную вставляешь такие строки, они начинают звучать не как данные, а как заметки автора в блокноте. Почти слышишь голос персонажа.
Пространство как сцена
Чтобы что-то происходило, нужно место. Добавим его:
INSERT INTO Places (id, name, description) VALUES (1, 'Офис', 'Открытое пространство с лампами дневного света'), (2, 'Кафе', 'Небольшое место с запахом корицы и кофе');
Тут и началась магия: SQL внезапно стал похож на описание сцены в пьесе. Если убрать ключевые слова и скобки, получится почти готовый кусочек прозы.
Сюжетные события
Обычная литература движется событиями, у нас — INSERT INTO Events.
INSERT INTO Events (id, place_id, character_id, action) VALUES (1, 1, 1, 'пишет SQL-запрос'), (2, 1, 2, 'смотрит на код и улыбается'), (3, 2, 3, 'шепчет комментарии с сарказмом');
В этот момент я впервые ощутил, что делаю нечто среднее между «базой данных для CRM» и «литературным редактором».
SELECT как рассказчик
Теперь соберём первые фразы.
SELECT c.name || ' (' || c.role || ', ' || c.mood || ')' || ' находится в ' || p.name || ' и ' || e.action AS narrative FROM Events e JOIN Characters c ON e.character_id = c.id JOIN Places p ON e.place_id = p.id;
Результат оказался неожиданно похож на черновик:
-
Алекс (программист, усталый) находится в Офис и пишет SQL-запрос
-
Ира (аналитик, вдохновлённая) находится в Офис и смотрит на код и улыбается
-
Бот (ИИ-ассистент, саркастичный) находится в Кафе и шепчет комментарии с сарказмом
SQL впервые заговорил «человеческим» языком.
Диалоги через UNION
Чтобы оживить историю, нужны диалоги. В SQL это делается через UNION.
SELECT c.name || ': "Хватит писать баги, пора писать истории!"' AS dialog FROM Characters c WHERE c.id = 1 UNION SELECT c.name || ': "Сюжет всегда живее цифр."' FROM Characters c WHERE c.id = 2 UNION SELECT c.name || ': "Запишу всё это в лог, на всякий случай."' FROM Characters c WHERE c.id = 3;
Получилось:
-
Алекс: «Хватит писать баги, пора писать истории!»
-
Ира: «Сюжет всегда живее цифр.»
-
Бот: «Запишу всё это в лог, на всякий случай.»
Никогда раньше не думал, что UNION может работать как драматург.
Конфликт как двигатель
Литература без конфликта мертва. Сделаем генератор конфликтов:
SELECT c1.name || ' спорит с ' || c2.name || ' о ' || (CASE WHEN RANDOM() % 2 = 0 THEN 'смысле данных' ELSE 'будущем технологий' END) AS conflict FROM Characters c1 JOIN Characters c2 ON c1.id < c2.id;
Каждый запуск давал разные сцены:
-
Алекс спорит с Ирой о смысле данных
-
Алекс спорит с Бот о будущем технологий
-
Ира спорит с Бот о смысле данных
Вот она, драматургия через псевдослучайность.
Финал рассказа
Историю нужно завершить. Пусть SQL решает за нас:
SELECT name || ' понимает, что ' || CASE mood WHEN 'усталый' THEN 'работа бесконечна' WHEN 'вдохновлённая' THEN 'даже данные можно превратить в искусство' ELSE 'машины тоже умеют иронизировать' END AS ending FROM Characters;
Финал получился неожиданно философским:
-
Алекс понимает, что работа бесконечна
-
Ира понимает, что даже данные можно превратить в искусство
-
Бот понимает, что машины тоже умеют иронизировать
Бонус: интеграция с Python
Чтобы история выглядела не только как таблица, я попробовал вытянуть её в текстовую новеллу через Python:
import sqlite3 conn = sqlite3.connect(":memory:") cursor = conn.cursor() # Тут код создания таблиц и вставки данных такой же, как выше for row in cursor.execute(""" SELECT c.name || ' (' || c.role || ', ' || c.mood || ')' || ' в ' || p.name || ' ' || e.action AS narrative FROM Events e JOIN Characters c ON e.character_id = c.id JOIN Places p ON e.place_id = p.id; """): print(row[0])
Когда выводишь это в консоль построчно, ощущение, что читаешь минималистичную новеллу. Ничего лишнего, только данные и их соединение.
Заключение
Можно ли написать рассказ на чистом SQL? Можно. И пусть это не станет бестселлером, но эксперимент показал, что язык запросов способен быть больше, чем инструментом для отчётов. SQL умеет быть и автором.
И, честно говоря, после этого опыта я стал относиться к базе данных чуть теплее. Вдруг где-то там, в её индексах, давно живёт маленький писатель?
ссылка на оригинал статьи https://habr.com/ru/articles/939882/
Добавить комментарий