Время прочтения: 10 мин.

Данные занимают центральное место в информационном обществе. Именно информация является основой для принятия решений, развития бизнеса и научных исследований. Многие организации инвестируют огромные средства в системы, способные хранить, обрабатывать и анализировать данные.

Одной из главных проблем, с которыми сталкиваются компании, это непосредственная работа с базами данных с помощью языка SQL (Structured Query Language). Не все знают этот язык, и не у всех есть время изучать его. Поэтому на рынке появляются инструменты, позволяющие писать запрос на естественном языке и получать ответ на языке запросов.

В посте познакомлю вас с двумя AI-инструментами для генерации SQL-запросов: SQLCoder Defog и SQLthroughAI.

Введение

Проблема преобразования запросов на естественном языке в SQL-запросы является одной из ключевых задач в области обработки естественного языка (Natural Language Processing, NLP). Одним из наиболее эффективных подходов к решению задачи text2sql является использование больших языковых моделей (Large Language Models, LLM). В научных публикациях используются различные наименования этой задачи: NL2SQLseq2sqlNLQ-SQL, NLQ2Querynatural language to SQL и прочее. В данном посте все эти термины являются синонимами.

Модели типа LLM for SQL-queries обучаются и тестируются на больших наборах пар запросов на естественном языке (natural language querying, NLQ) и языке SQL, которые способны извлекать информацию из одной или нескольких баз данных. Часто используются такие наборы данных, как SpiderWikiSQLSParC и другие.

Запрос text-to-SQL обычно состоит минимум из двух компонентов: вопроса на естественном языке и описания (схемы) базы данных. Необязательными элементами являются демонстрационный пример и инструкции по выполнению задачи.

Основными проблемами применения LLM for SQL-queries являются генерирование ошибочных ответов (галлюцинации модели) и риск похищения пользовательских данных. Пользователи должны самостоятельно проверять выдаваемый моделью SQL-запрос, поскольку нет никаких гарантий, что скрипт будет рабочий. Вторая же проблема решается созданием собственной LLM или размещением автономной (self-hosted) модели на собственном сервере.

Сейчас на рынке доступно множество решений так или иначе связанных с генерацией SQL-запросов: DataheraldDB-GBTNL2SQL и другие.

Я сосредоточилась на модели SQLCoder Defog, а для сравнения с ней выбрала SQLthroughAI, использующую технологии от Open AI.

Немного о моделях

SQLCoder — это большая языковая модель с 15 миллиардами параметров, дообученная на модели StarCoder компанией Defog. Модель прошла обучение на 10 537 вопросах, заданных людьми на основе 10 различных схем баз данных. Обучение проходило в два этапа в зависимости от уровня сложности вопросов:

  • первый этап: «легкие» и «средние» вопросы (easy+medium);
  • второй этап: «сложные» и «сверхсложные» вопросы (hard+extra-hard).

Эта классификация была выполнена путем адаптации рубрикации, используемой набором данных Spider. Результаты обучения на данных, взятых с первого этапа, были сохранены в модели под названием defog-easy. Для оценивания корректности предлагаемых запросов компания разработала свой собственный фреймворк – sql-eval.

SQLCoder превосходит gpt-3.5-turbo и text-davinci-003, которые являются моделями, более чем в 10 раз превышающими его размер, а при тонкой настройке для отдельных схем баз данных SQLCoder будет обладать такой же или лучшей производительностью, чем GPT-4 OpenAI, с меньшей задержкой (на A100 GPU).

Способы использования SQLCoder:

  • непосредственно скачать из Hugging Face;
  • воспользоваться демо-версией через веб-сайт;
  • запустить блокнот в Colab.

SQLthroughAI — это инструмент искусственного интеллекта, созданный на базе технологий от Open AI. На сайте представлено крайне мало информации, поэтому неизвестно, как именно была доработана модель, но за этой информацией можно обратиться к разработчикам: SQLtroughAI@gmail.com.

Почему я выбрала этот AI-помощник? Он бесплатный, не требует API key и имеет веб-интерфейс.

Практическая часть

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

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

Хочу отметить, что несмотря на информацию, представленную на сайте о том, что в настоящее время SQLthroughAI поддерживает только английский язык, AI-помощник принимал запросы на русском языке и выдавал корректные SQL-ответы.

Все задания взяты c сайта sql-academy. Выберу базу данных «Авиаперелеты» и продемонстрирую работу большой языковой модели.

Схема базы данных во всех запросах одинаковая. Создам её с помощью DDL (языка описания данных). Также перед промтом для модели SQLCoder буду писать MySQL, поскольку сайт sql-academy принимает ответы только на этом диалекте.

Для SQLthroughAI создам схему базы данных вручную и настрою связи с помощью веб-интерфейса. При отправке промта необходимо выбирать все таблицы из базы данных, и отмечать необходимый диалект, в моём случае — MySQL.

Сразу отмечу, что на сайте был представлен единственный сложный запрос для данной таблицы. Поэтому я попросила AI-инструменты сгенерировать сложные запросы и для другой базы данных Airbnb.

Авиаперелеты

SQLCoder

CREATE TABLE trip(
id INT PRIMARY KEY,
company INT, -- идентификатор компании-перевозчика
plane VARCHAR, -- модель самолета
town_from VARCHAR, -- город вылета
town_to VARCHAR, -- город прилёта
time_out DATETIME, -- время вылета
time_in DATETIME -- время прилёта
); 

CREATE TABLE company(
id INT PRIMARY KEY,
name VARCHAR -- название компании-перевозчика
);

CREATE TABLE pass_in_trip(
id INT PRIMARY KEY,
trip INT, -- идентификатор билета
passenger INT, -- идентификатор пассажира
place VARCHAR -- место пассажира в самолёте
);

CREATE TABLE passenger(
id INT PRIMARY KEY,
name VARCHAR -- имя и фамилия пассажира
);

SQLthroughAI

Интерфейс для создания полей в таблице. Предлагается всего 4 типа поля (string, date, number, id).

Создаю таблицы Авиаперелеты

Настрою связи между таблицами

Airbnb

SQLCoder

-- отзывы на арендуемое жилье
CREATE TABLE Reviews( 
id INT PRIMARY KEY,
reservation_id INT, -- идентификатор брони
rating INT, -- рейтинг от 1 до 5
); 

-- пользователи сервиса бронирования
CREATE TABLE users(
id INT PRIMARY KEY,
name VARCHAR, -- имя и фамилия пользователя
email VARCHAR, -- электронная почта пользователя
email_verified_at DATETIME, -- дата подтверждения почты
password VARCHAR, -- пароль от аккаунта
phone_number VARCHAR -- номер телефона пользователя
);

-- история бронирования жилья
CREATE TABLE Reservations(
id INT PRIMARY KEY,
user_id INT, -- идентификатор  пользователя
room_id INT, -- идентификатор  жилья
start_date DATETIME, -- дата начала бронирования
end_date DATETIME, -- дата окончания бронирования
price INT, -- цена за сутки
total INT -- общая стоимость бронирования
);

-- доступные вариант жилья
CREATE TABLE Rooms(
id INT PRIMARY KEY,
home_type VARCHAR, -- тип жилья
address VARCHAR, -- адрес жилья
has_tv BOOLEAN, -- наличие телевизора
has_internet BOOLEAN, -- наличие интернета
has_kitchen BOOLEAN, -- наличие кухни
has_air_con BOOLEAN, -- наличие кондиционера
price INT, -- цена за сутки
owner_id INT, -- идентификатор владельца
latitude FLOAT, -- географическая широта
longitude FLOAT -- географическая долгота
);

SQLthroughAI

Создаю базу данных Airbnb

Настраиваю связи между таблицами

Выбираю все таблицы из базы данных для формирования запроса

Примеры запросов

№ 55:

NL-query (запрос на естественном языке)
Удалить компании, совершившие наименьшее количество рейсов.
SQL query (полученный SQL-запрос)
SQLCoder (неверно)SQLthroughAI (верно)
WITH min_trip_count AS (SELECT company, count(*) AS trip_countFROM tripGROUP BY companyORDER BY trip_count ASCLIMIT 1)DELETE FROM companyWHERE id IN (SELECT companyFROM min_trip_count)DELETE FROM companyWHERE id IN(SELECT t.companyFROM trip tGROUP BY t.companyHAVING COUNT(*) =(SELECT MIN(count_flights)FROM (SELECT COUNT(*) AS count_flightsFROM tripGROUP BY company ) AS subquery ) );

Комментарий

В чём же проблема решения от SQLCoder? Модель считает, что только одна компания совершает наименьшее количество рейсов. К сожалению, даже корректировка запроса не помогла решить данную задачу.

№ 58:

NL-query
Добавить отзыв с рейтингом 5 на жилье, находящиеся по адресу «11218, Friel Place, New York», от имени «George Clooney».В качестве первичного ключа (id) укажите количество записей в таблице + 1.Резервация комнаты, на которую вам нужно оставить отзыв, уже была сделана, нужно лишь её найти.
SQL query
SQLCoder (верно)SQLthroughAI (неверно)
INSERT INTO reviews (id, rating, reservation_id)VALUES ((SELECT COUNT(*) + 1 FROM reviews as a),5,(SELECT r.idFROM reservations rJOIN rooms r1 on r.room_id = r1.idWHERE r1.address = '11218, Friel Place, New York'and r.user_id = (SELECT u.id FROM users u WHERE u.name = 'George Clooney')));INSERT INTO Reviews (id, reservation_id, rating)VALUES ((SELECT COUNT(*)+1 FROM Reviews),(SELECT idFROM ReservationsWHERE room_id = (SELECT id FROM Rooms WHERE address = "11218, Friel Place, New York")),5);

Комментарий

SQLthroughAI не учитывает условие, связанное с пользователем (name = 'George Clooney'), и поэтому даже не связывает необходимые таблицы.

№ 68:

NL-query
Для каждой комнаты, которую снимали как минимум 1 раз, найдите имя человека, снимавшего ее последний раз, и дату, когда он выехал.Поля в результирующей таблице:room_idnameend_dateИспользуйте конструкцию «as room_id» для вывода идентификатора комнаты.
SQL query
SQLCoder (верно)SQLthroughAI (верно)
WITH last_seen AS (SELECT r.room_id, u.name, r.end_date FROM reservations r join users u on r.user_id = u.idWHERE r.start_date = (SELECT max(start_date)FROM reservationsWHERE room_id = r.room_id))SELECT l.room_id, l.name, l.end_dateFROM last_seen lGROUP BY l.room_id, l.name, l.end_dateHAVING count(*) >= 1;SELECT r.room_id AS room_id,u.name AS name,r.end_date AS end_dateFROM Reservations rJOIN users u ON r.user_id = u.idWHERE r.room_id IN(SELECT room_idFROM ReservationsGROUP BY room_idHAVING COUNT(DISTINCT user_id) > 0 ) AND r.id = ( SELECT id FROM Reservations r2 WHERE r2.room_id = r.room_id ORDER BY end_date DESC LIMIT 1 )

Комментарий

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

№ 69:

Мне кажется, что данное задание составлено некорректно. В итоговом решении требуется замена NULL на нулевые значения, поэтому было принято решение скорректировать запрос на естественном языке.

Модели плохо справлялась с данным заданием, поэтому явно указала необходимую конструкцию.

NL-query
Вывести идентификаторы всех владельцев комнат и сумму, которую они заработали. Суммирование происходит по полю total.Замени все NULL в поле суммы на ноль с помощью COALESCE.Используйте конструкцию «as owner_id» и «as total_earn» для вывода идентификаторов владельцев и заработанной суммы соответственно.
SQL query
SQLCoder (верно)SQLthroughAI (верно)
SELECT rooms.owner_id as owner_id,COALESCE(sum(reservations.total),0) as total_earnFROM rooms LEFT JOIN reservationsON rooms.id = reservations.room_idGROUP BY rooms.owner_id;SELECT owner_id as owner_id, COALESCE(SUM(total), 0) as total_earnFROM RoomsLEFT JOIN Reservations ON Rooms.id = Reservations.room_idGROUP BY owner_id

Комментарий

Обе модели успешно справились с заданием.

№ 71:

NL-query
Найдите какой процент пользователей, зарегистрированных на сервисе бронирования, хоть раз арендовали или сдавали в аренду жилье. Результат округлите до сотых.Поля в результирующей таблице:percentИспользуйте конструкцию «as percent» для вывода процента активных пользователей. Пример формата ответа — 65.23.
SQL query
SQLCoder (неверно)SQLthroughAI (неверно)
WITH active_users AS (SELECT count(*) AS active_users_countFROM usersWHERE id IN (SELECT DISTINCT user_idFROM reservations))SELECT ROUND(100.0 * active_users.active_users_count / (SELECT count(*)FROM users), 2) AS percentFROM active_users;SELECT (COUNT(DISTINCT u.id) / COUNT(DISTINCT r.user_id) * 100) AS percentFROM users uLEFT JOIN Reservations r ON u.id = r.user_idWHERE u.email_verified_at IS NOT NULLAND r.user_id IS NOT NULLAND (r.start_date IS NOT NULL OR r.end_date IS NOT NULL)

Комментарий

Как видно, обе модели не учитывают владельцев жилья, также SQLthroughAI не использует команду округления.

Оценка моделей

МодельБаза данныхУровень сложностиВерно (номера заданий)Неверно (номера заданий)Процент верных
SQLCoderПерелётыЛёгкие2, 3, 4, 5, 6, 7, 9, 28, 561, 12, 14, 1517/26≈65%
Средние8, 13, 16, 29, 3010, 11, 67
Сложные55
AirbnbСложные58, 68, 6971
SQLthroughAIПерелётыЛёгкие2, 3, 4, 5, 7, 9, 14, 15, 28, 561, 6, 1218/26≈69%
Средние10, 11, 16, 29, 308, 13, 67
Сложные55
AirbnbСложные68, 6958, 71

Как видно, каждая из моделей выполнила примерно две трети запросов успешно.

SQLCoderSQLthroughAI
Лёгкие≈ 69,2%≈ 76,9%
Средние≈ 62,5%≈ 62,5%
Тяжелые≈ 60%≈ 60%

Сравнение моделей

В таблице я сравнила модели и результаты их работы:

SQLCoderSQLthroughAI
Способы использованияHugging Face; Colab; веб-сайт.Веб-сайт.
Возможность доработки моделиДаНет
Поддерживаемые базы данныхMySQL, PostgreSQL, Amazon Redshift, Showflake, BigQuery, Azure SQLMySQL, PostgreSQL, MS SQL, Mongo DB, Oracle PL/SQL, BigQuery, MariaDB
Способы создания базы данныхDDLCSV/Excel; DDL; вручную на сайте
Настройка типов данныхВсе известные типыString, Date, Number, ID
РегистрацияНетДа
Процент полученных верных ответов65%69%

Вывод

SQLCoder и SQLthroughAI частично справились с задачей преобразования запроса на естественном языке в запрос на языке SQL. Не рекомендую пользователям без опыта в данной области применять эти AI-помощники, поскольку почти в трети случаев всё ещё совершаются ошибки, и это может быть критично для ваших данных. Тем не менее, как помощник или генератор идей эти инструменты могут быть полезными: инструменты могут предложить пользователю различные варианты запросов и помочь синтаксически правильно составить их.