Технологии

SQL-запросы для новичка: как читать и писать

32 минАктуально на 11 сентября 2026

SQL-запросы для новичка

Когда Claude Code подключает к приложению базу данных, в проекте появляются команды вроде SELECT, INSERT и UPDATE. Учить SQL как отдельную профессию не требуется, но нужно уметь прочитать сгенерированный запрос до запуска и понять, какие данные он найдёт, добавит или удалит. Особенно это важно для команд, которые меняют сразу много строк: одна пропущенная часть запроса способна испортить всю таблицу рекордов.

Содержание
  1. Что такое SQL и зачем он новичку, если код пишет ИИ
  2. Как устроена таблица SQL: строки, столбцы и первичный ключ
  3. Четыре главных SQL-запроса: SELECT, INSERT, UPDATE и DELETE
  4. Фильтры и сортировка: WHERE, ORDER BY и LIMIT
  5. Связи между таблицами: внешний ключ и JOIN
  6. Агрегаты COUNT, AVG, MAX и GROUP BY
  7. Где выполнять SQL-запросы
  8. Как происходит выполнение SQL-запросов
  9. Типичные ошибки новичка в SQL-запросах
  10. Безопасность: SQL-инъекция и параметризованные запросы
  11. Как просить ИИ написать SQL-запрос
  12. Как применить и проверить SQL-запрос без риска
  13. Частые вопросы

Что такое SQL и зачем он новичку, если код пишет ИИ

SQL расшифровывается как Structured Query Language — язык структурированных запросов. Это язык, с помощью которого приложение разговаривает с реляционной базой данных: просит показать записи, добавить новую, исправить существующую или удалить ненужную. PostgreSQL, который часто используют вместе с Supabase, понимает SQL.

База данных хранит сведения не внутри страницы игры и не в памяти браузера, а в отдельной системе. Благодаря этому рекорды можно открыть с другого устройства, показать в общей таблице лидеров и связать с аккаунтами игроков. Если хочется сначала разобраться в самой идее хранения информации, пригодится разбор баз данных для новичка. А особенности конкретной системы описаны в материале PostgreSQL для новичка.

SQL-запрос — это текстовая команда. Она отвечает на три вопроса:

  • что сделать с данными;
  • с какой таблицей работать;
  • какие строки затронуть.

Например, команда ниже читается почти как фраза: «выбери имя и счёт из таблицы игроков, где счёт больше 100».

SELECT name, score
FROM players
WHERE score > 100;

Точка с запятой завершает команду. В некоторых редакторах один запрос выполнится и без неё, но ставить её полезно: так границы команд видны и человеку, и инструменту.

Claude Code способен написать такой код за секунды. Твоя роль — не вспоминать синтаксис по памяти, а проверить смысл до выполнения. Посмотри, к какой таблице обращается запрос, какие столбцы читает или меняет и есть ли ограничение на строки. Это похоже на проверку документа перед подписью: текст подготовил помощник, но последствия относятся к твоим данным.

Запросы бывают читающими и изменяющими. SELECT только получает данные. INSERT, UPDATE и DELETE меняют содержимое таблицы. Такое разделение помогает оценивать риск: ошибочный SELECT обычно вернёт не тот результат, а ошибочный DELETE может безвозвратно удалить записи, если нет резервной копии.

SQL не заменяет весь код приложения. JavaScript реагирует на нажатия, двигает змейку и отправляет запросы; база данных принимает запрос, проверяет права и работает с записями. Обычно ты увидишь SQL в миграциях, серверных функциях, настройках базы и ответах ИИ. Даже когда библиотека скрывает SQL за методами вроде .select() или .insert(), смысл операций остаётся тем же.

Как устроена таблица SQL: строки, столбцы и первичный ключ

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

Возьмём таблицу рекордов «Змейки» с именем players:

id name score created_at
1 Алина 180 2026-09-08 14:20:00
2 Максим 95 2026-09-09 09:15:00
3 Олег 240 2026-09-10 21:04:00

Одна строка — одна запись об игроке. Столбцы означают следующее:

  • id — уникальный номер записи;
  • name — имя игрока;
  • score — число набранных очков;
  • created_at — дата и время создания записи.

У столбца есть тип данных. id и score хранят числа, name — текст, created_at — дату со временем. Тип не даёт случайно записать слово много туда, где база ожидает число. Конкретные названия типов зависят от системы, но идея общая: база должна понимать, как сравнивать, сортировать и проверять значения.

id в примере — первичный ключ, или PRIMARY KEY. Это столбец, значение которого однозначно указывает на строку. Двух игроков с одинаковым именем может быть сколько угодно, а двух строк с одним и тем же первичным ключом быть не должно. Поэтому для изменения конкретной записи безопаснее использовать WHERE id = 3, а не WHERE name = 'Олег'.

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

Название таблицы и названия столбцов образуют её схему — договор о том, как устроены данные. Если код обращается к points, а в таблице есть только score, база вернёт ошибку. По этой причине перед проверкой сгенерированного запроса полезно открыть схему или попросить Claude Code сначала перечислить таблицы, столбцы, типы и связи, на которые он опирается.

Строка может содержать NULL — отсутствие значения. Это не пустая строка и не ноль. Например, created_at может временно не иметь значения, если его заполнение не настроено. Проверка на отсутствие записывается как IS NULL, а не = NULL:

SELECT id, name
FROM players
WHERE created_at IS NULL;

Полезно различать саму таблицу и результат запроса. Таблица players хранится в базе постоянно, а результат SELECT — временная выборка: только нужные строки и столбцы. Если запрос показал десять лучших результатов, остальные записи никуда не исчезли.

Четыре главных SQL-запроса: SELECT, INSERT, UPDATE и DELETE

Большая часть повседневной работы сводится к четырём операциям. Их часто обозначают сокращением CRUD: создание, чтение, изменение и удаление. Запоминать сокращение необязательно; важнее узнавать действие по первому слову запроса.

Запрос Что делает Пример
SELECT Читает данные Показать имена и рекорды
INSERT Добавляет строку Сохранить нового игрока
UPDATE Изменяет строки Обновить результат игрока
DELETE Удаляет строки Удалить ошибочную запись

SELECT — получить данные

SELECT выбирает столбцы, которые нужно вернуть. После FROM указывается таблица.

SELECT id, name, score, created_at
FROM players;

Такой запрос данных SQL вернёт все строки из players, но саму таблицу не изменит. Звёздочка в SELECT * означает «все столбцы». Для быстрой проверки это удобно, однако в коде приложения лучше перечислять нужные столбцы: результат понятнее, а база не передаёт лишнее.

Читай запрос по частям: SELECT id, name, score — какие поля получить; FROM players — откуда их взять. Если дальше есть WHERE, ORDER BY или LIMIT, они уточняют выборку.

INSERT — добавить строку

INSERT INTO создаёт новую запись. Сначала перечисляются столбцы, затем в том же порядке — значения.

INSERT INTO players (name, score)
VALUES ('Ирина', 130);

База сопоставит 'Ирина' со столбцом name, а 130 — со score. id можно не передавать, если он создаётся автоматически. created_at тоже можно не указывать, если у столбца настроено значение по умолчанию.

Порядок имеет значение. Запрос (score, name) VALUES ('Ирина', 130) ошибочен: текст попадёт на место числа. Явный список столбцов защищает от подобных недоразумений и делает команду читаемой.

UPDATE — изменить существующие строки

UPDATE выбирает таблицу, SET задаёт новые значения, а WHERE определяет строки для изменения.

UPDATE players
SET score = 165
WHERE id = 4;

Запрос меняет счёт только у записи с id = 4. Если подходящей строки нет, база не создаст новую — изменится ноль строк. Если условию соответствуют несколько строк, изменятся все они.

Перед выполнением незнакомого UPDATE замени начало на SELECT * и оставь тот же WHERE:

SELECT *
FROM players
WHERE id = 4;

Так ты увидишь будущую цель изменения без риска для данных. После выполнения проверь не только сообщение об успехе, но и количество затронутых строк.

DELETE — удалить строки

DELETE FROM удаляет строки, подходящие под условие.

DELETE FROM players
WHERE id = 4;

Команда не удаляет отдельную ячейку: она удаляет строку целиком. Чтобы очистить только значение столбца, используют UPDATE, например SET name = NULL, если схема разрешает отсутствие имени.

С DELETE действует та же проверка: сначала выполни SELECT с тем же WHERE. Для важных данных нужны резервные копии и возможность восстановления. Кнопка «выполнить» не обязана спрашивать дополнительное подтверждение.

Фильтры и сортировка: WHERE, ORDER BY и LIMIT

Без фильтра запрос работает со всей таблицей. WHERE отбирает строки по условию, ORDER BY задаёт их порядок, LIMIT ограничивает количество строк в результате. Вместе они решают задачу «показать десять лучших результатов»:

SELECT name, score, created_at
FROM players
ORDER BY score DESC, created_at ASC
LIMIT 10;

score DESC ставит больший счёт выше меньшего. Если результаты равны, created_at ASC ставит раньше созданную запись первой. Такое второе правило делает порядок предсказуемым. Без него база вправе возвращать строки с одинаковым счётом в разной последовательности.

ASC означает сортировку по возрастанию, DESC — по убыванию. Для чисел возрастание идёт от меньшего к большему, для дат — от ранних к поздним. Если направление не написать, обычно используется ASC, но явная запись читается легче.

Чтобы показать десять лучших результатов только среди тех, кто набрал не меньше 100 очков, добавь фильтр:

SELECT name, score
FROM players
WHERE score >= 100
ORDER BY score DESC
LIMIT 10;

Условия можно соединять словами AND и OR:

SELECT id, name, score
FROM players
WHERE score >= 100 AND name = 'Ирина';

AND требует выполнения обоих условий. OR достаточно одного. Когда они встречаются вместе, ставь скобки и выражай намерение явно:

SELECT id, name, score
FROM players
WHERE score >= 100
  AND (name = 'Ирина' OR name = 'Алина');

Для диапазона можно использовать два сравнения:

SELECT name, score
FROM players
WHERE score >= 100 AND score <= 200;

LIMIT ограничивает только результат чтения. Он не означает, что база просмотрит случайные десять строк и затем отсортирует их: логически сначала формируется и сортируется подходящий набор, затем остаётся указанное количество. Но LIMIT без ORDER BY не определяет, какие именно строки попадут в ответ.

С командами изменения WHERE ещё важнее. Условие WHERE id = 4 затронет одну конкретную запись, а WHERE score < 0 — все ошибочные записи с отрицательным счётом. Всегда проговаривай условие обычными словами: «изменить все строки, где…». Если фраза звучит шире задачи, запрос тоже слишком широкий.

Связи между таблицами: внешний ключ и JOIN

Одна таблица не обязана хранить всё. Если игрок может провести много партий, запись каждой партии удобнее держать отдельно. Таблица players описывает игроков, а games — отдельные партии:

id player_id score played_at
101 1 120 2026-09-08 13:50:00
102 1 180 2026-09-08 14:20:00
103 3 240 2026-09-10 21:04:00

games.player_id — внешний ключ. Он хранит id игрока из таблицы players и связывает партию с её владельцем. Если player_id = 1, партия относится к Алине, потому что в players у Алины id = 1.

Ограничение внешнего ключа не даёт сослаться на несуществующего игрока. База отклонит партию с player_id, которого нет в players. Поведение при удалении самого игрока настраивается отдельно: связанные партии могут удаляться, блокировать удаление или терять ссылку. Не соглашайся на выбранное ИИ поведение, пока не понял, что должно происходить с историей партий.

JOIN объединяет связанные строки в одном результате. Чтобы показать имя игрока рядом с каждой партией, запрос соединяет players и games по равным идентификаторам:

SELECT p.name, g.score, g.played_at
FROM players AS p
JOIN games AS g ON g.player_id = p.id
ORDER BY g.played_at DESC;

p и g — короткие псевдонимы таблиц. p.name означает столбец name из players, а g.scorescore из games. Условие после ON объясняет базе, какие строки считать связанными.

Читай этот запрос так: «получи имя из игроков и результат с датой из партий; соедини партию с тем игроком, чей id записан в player_id; покажи новые партии первыми». На старте этого понимания достаточно — углубляться в разновидности JOIN не нужно.

Если соединить таблицы без правильного условия, каждая строка одной таблицы может сочетаться со многими строками другой. Результат раздуется и будет содержать неверные пары. При проверке запроса найди ON и убедись, что внешний ключ сравнивается с первичным: games.player_id = players.id.

В учебной схеме players.score может хранить лучший текущий результат, а games.score — результат каждой отдельной партии. Эти два столбца не противоречат друг другу, если их смысл записан явно. Но обновление лучшего результата и сохранение партии тогда должны происходить согласованно, иначе значения разойдутся.

Агрегаты COUNT, AVG, MAX и GROUP BY

Агрегатная функция берёт набор строк и вычисляет одно итоговое значение. Вместо списка всех партий можно узнать их количество, средний счёт или максимум.

COUNT считает строки:

SELECT COUNT(*) AS games_count
FROM games;

AS games_count даёт результату понятное имя. Без псевдонима редактор покажет техническое название выражения.

AVG вычисляет среднее значение, а MAX — наибольшее:

SELECT AVG(score) AS average_score,
       MAX(score) AS best_score
FROM games;

Этот запрос даёт показатели по всем партиям вместе. Чтобы получить средний счёт по каждому игроку, строки нужно разделить на группы. Для этого служит GROUP BY:

SELECT p.id,
       p.name,
       COUNT(g.id) AS games_count,
       AVG(g.score) AS average_score,
       MAX(g.score) AS best_score
FROM players AS p
JOIN games AS g ON g.player_id = p.id
GROUP BY p.id, p.name
ORDER BY average_score DESC;

База сначала соединяет игроков с партиями, затем собирает строки отдельно для каждого сочетания p.id и p.name, после чего считает показатели внутри каждой группы. Результат может выглядеть так:

id name games_count average_score best_score
3 Олег 4 172.5 240
1 Алина 3 150 180

Почему в GROUP BY попали p.id и p.name? В выборке рядом с агрегатами стоят обычные столбцы. Базе нужно явно сказать, для какой группы показывать каждое имя. Идентификатор нужен ещё и потому, что имена могут совпадать: две Ирины должны остаться двумя игроками, а не слиться в одну группу.

COUNT(*) считает строки, включая строки с пустыми значениями в отдельных столбцах. COUNT(g.id) считает только строки, где g.id не равен NULL. В обычном соединении партий с игроками результат чаще совпадает, но разница становится существенной в других вариантах соединения.

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

Агрегаты отвечают на вопросы о группе, но не заменяют исходные строки. MAX(score) сообщает лучший счёт, однако сам по себе не говорит, кто и когда его получил. Для этого нужны дополнительные столбцы, соединение или отдельный запрос.

Где выполнять SQL-запросы

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

Консоль PostgreSQL psql

psql — консольная программа для PostgreSQL. Она подходит, когда Claude Code работает в терминале, выполняет миграцию или помогает быстро проверить запрос. Подключение часто задают строкой соединения:

psql "$DATABASE_URL"

DATABASE_URL здесь — переменная окружения, а не значение, которое нужно вставлять в статью, сообщение или репозиторий. Строка подключения может содержать пароль. Не проси ИИ печатать её в отчёте и не добавляй её в Git.

Внутри psql можно посмотреть таблицы и описание конкретной таблицы:

\dt
\d players

Команды с обратной косой чертой относятся к psql, а не к самому SQL. Запрос SELECT будет работать и в других редакторах, а \dt — только в этой консольной оболочке.

SQL-редактор Supabase

В Supabase есть браузерный SQL-редактор. Он удобен для просмотра результата, создания таблиц и применения подготовленных изменений. Названия и расположение элементов интерфейса могут меняться, поэтому ориентируйся на назначение редактора, а не на последовательность кнопок.

Перед запуском проверь, к какому проекту и окружению подключён редактор. Тестовая и рабочая базы могут выглядеть одинаково, но цена ошибки различается. Запрос без WHERE, выполненный в рабочем проекте, не становится безопаснее оттого, что его написал ИИ.

Доступ пользователя приложения к строкам Supabase может дополнительно ограничиваться политиками Row Level Security. Это отдельный уровень защиты: SQL-запрос может быть синтаксически правильным, но политика не разрешит чтение или изменение строки. Разобраться с этим поможет материал про RLS-политики Supabase.

Любой GUI для базы данных

GUI — программа с графическим интерфейсом для подключения к базе. В ней обычно есть дерево таблиц, окно SQL и таблица результата. Такой инструмент удобен, если хочется видеть структуру без консольных команд.

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

Для первых опытов лучше использовать отдельную тестовую таблицу или тестовый проект. Добавь несколько вымышленных записей, выполни SELECT, затем попробуй безопасные изменения по конкретному id. Навык формируется быстрее, когда результат каждого действия можно сразу сравнить с ожиданием.

Как происходит выполнение SQL-запросов

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

Для чтения полезен логический порядок частей SELECT:

  1. FROM — определить источник данных;
  2. JOIN и ON — соединить связанные таблицы;
  3. WHERE — отфильтровать строки;
  4. GROUP BY — собрать группы;
  5. SELECT — сформировать столбцы результата;
  6. ORDER BY — отсортировать;
  7. LIMIT — оставить нужное количество.

В тексте SELECT написан первым, потому что так устроен синтаксис. Но знание логического порядка объясняет частые ошибки. Например, псевдоним агрегата, созданный в SELECT, обычно можно использовать в ORDER BY, но не всегда в WHERE: фильтрация произошла раньше вычисления этого псевдонима.

Успешное выполнение означает только то, что база приняла команду. Оно не доказывает, что команда соответствует задаче. UPDATE players SET score = 0; синтаксически безупречен и при этом обнулит все результаты. Поэтому проверка смысла важнее зелёного сообщения редактора.

Изменения, которые должны сработать вместе, можно выполнять в транзакции. Транзакция — группа команд по принципу «всё или ничего»:

BEGIN;

INSERT INTO games (player_id, score)
VALUES (3, 260);

UPDATE players
SET score = 260
WHERE id = 3 AND score < 260;

COMMIT;

Если до COMMIT обнаружилась проблема, изменения можно отменить командой ROLLBACK вместо подтверждения. Транзакция полезна, когда сохранение партии и обновление рекорда образуют одно действие. Но она не заменяет проверку условий: после COMMIT обычный ROLLBACK уже не вернёт прошлое состояние.

На больших таблицах скорость зависит от индексов — специальных структур для быстрого поиска. Первичный ключ обычно индексируется, поэтому поиск по id эффективен. Новичку не нужно добавлять индекс к каждому столбцу: лишние индексы занимают место и замедляют запись. Если запрос стал медленным, попроси ИИ показать план выполнения и объяснить, какой индекс он предлагает и почему.

Типичные ошибки новичка в SQL-запросах

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

Забытый WHERE в UPDATE или DELETE

Самая рискованная ошибка выглядит коротко:

UPDATE players
SET score = 0;

Здесь нет WHERE, поэтому новый счёт получит каждая строка. Аналогично DELETE FROM players; удалит всех игроков. Если задача относится не ко всей таблице, отсутствие фильтра — причина остановиться.

Безопасная привычка состоит из четырёх действий:

  1. Сформулируй словами, какие строки должны измениться.
  2. Выполни SELECT с тем же условием.
  3. Сверь найденные строки и их количество.
  4. Выполни изменение и проверь количество затронутых строк.

Для ответственной операции попроси Claude Code использовать транзакцию и оставить подтверждение COMMIT тебе. Но сначала убедись, что выбранный инструмент действительно сохраняет транзакцию открытой между командами.

Одинарные и двойные кавычки

В SQL одинарные кавычки обозначают текстовое значение:

SELECT *
FROM players
WHERE name = 'Ирина';

Двойные кавычки обозначают идентификатор — имя таблицы или столбца:

SELECT "name", "score"
FROM "players";

Запись WHERE name = "Ирина" неверна по смыслу: PostgreSQL попробует найти столбец с именем Ирина, а не текст. Для обычных имён в нижнем регистре двойные кавычки не нужны.

Апостроф внутри текста удваивается: имя О'Нил в SQL-литерале выглядело бы как 'О''Нил'. Но пользовательские значения нельзя вручную превращать в готовый SQL даже с таким экранированием. Для них нужны параметры, о которых речь пойдёт в разделе безопасности.

Регистр в именах

Ключевые слова SQL обычно нечувствительны к регистру: select и SELECT означают одно действие. Заглавные буквы используют для читаемости, чтобы команды отличались от имён таблиц и столбцов.

В PostgreSQL идентификаторы без двойных кавычек приводятся к нижнему регистру. Если создать столбец как "playerName", обращаться к нему придётся с тем же регистром и кавычками. Поэтому простые имена вроде player_name, created_at и games удобнее смешанного регистра.

Перепутанные столбцы и значения

В INSERT значение соответствует столбцу по позиции. Проверяй пары слева направо:

INSERT INTO players (name, score)
VALUES ('Лена', 210);

Если список большой, переноси пары на строки или попроси ИИ прокомментировать соответствие. Не полагайся на порядок столбцов внутри таблицы и всегда перечисляй их явно.

Сравнение с NULL через знак равенства

NULL означает неизвестное или отсутствующее значение. Условие created_at = NULL не даст ожидаемого результата. Используй created_at IS NULL или created_at IS NOT NULL.

LIMIT без ORDER BY

Запрос SELECT * FROM players LIMIT 10; вернёт десять строк, но не обязательно лучших, первых созданных или последних. Чтобы смысл был определён, добавь порядок, например ORDER BY score DESC.

Слишком широкая выборка

SELECT * кажется универсальным, но скрывает намерение. Если экрану нужны только имя и счёт, запроси name, score. Это облегчает чтение, сокращает передаваемые данные и не раскрывает приложению лишние поля.

Безопасность: SQL-инъекция и параметризованные запросы

SQL-инъекция возникает, когда пользовательский ввод склеивают с текстом запроса. Тогда введённая строка может превратиться не в обычное значение, а в часть команды для базы.

Опасный подход выглядит так:

const sql = "SELECT id, name, score FROM players WHERE name = '" + playerName + "'";

Разработчик ожидает, что playerName содержит имя. Но злоумышленник может ввести кавычки и фрагмент SQL, меняющий смысл условия. База не знает, какая часть строки пришла от пользователя: после склейки для неё всё выглядит одной командой.

Правильный подход — параметризованный запрос. Текст команды и пользовательское значение передаются отдельно:

const result = await db.query(
  'SELECT id, name, score FROM players WHERE name = $1',
  [playerName]
);

$1 — место для первого параметра. Драйвер базы передаст playerName как значение, а не как исполняемый SQL. Даже если внутри окажутся кавычки или слова DELETE, они останутся обычным текстом.

Синтаксис параметров зависит от библиотеки: встречаются $1, ? или именованные обозначения. Принцип один — значения не склеиваются со строкой запроса. Если запрос собирается из пользовательского ввода, параметризация обязательна.

Параметры защищают значения, но имена таблиц, столбцов и направление сортировки часто нельзя передать тем же способом. Если пользователь выбирает сортировку, приложение должно сопоставить его выбор с заранее разрешённым списком:

const allowedSorts = {
  score: 'score DESC',
  newest: 'created_at DESC'
};

const orderBy = allowedSorts[userSort] ?? allowedSorts.score;
const sql = `SELECT name, score FROM players ORDER BY ${orderBy} LIMIT $1`;

Здесь в SQL попадает только одна из заранее написанных разработчиком строк. Произвольный пользовательский текст не становится именем столбца или командой.

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

Не вставляй в запрос к Claude Code настоящие пароли, ключи API, строки подключения или выгрузки с личными данными. Для объяснения достаточно вымышленных имён, схемы таблиц и обезличенных примеров. Секреты хранятся в переменных окружения и настройках сервиса, а не в SQL-файлах репозитория.

Как просить ИИ написать SQL-запрос

Фраза «напиши какой-нибудь SQL-запрос для рекордов» оставляет слишком много решений на усмотрение ИИ. Хорошая постановка содержит схему, требуемый результат, ограничения и формат ответа.

Пример задания для Claude Code:

💡

В PostgreSQL есть таблицы players(id, name, score, created_at) и games(id, player_id, score, played_at). games.player_id ссылается на players.id. Напиши один параметризованный запрос, который по player_id возвращает имя игрока, количество партий, средний и максимальный счёт. Запрос не должен менять данные. Используй параметр $1, объясни каждую часть простыми словами и укажи, какой результат вернётся, если партий нет. Ничего не выполняй.

Такой запрос задаёт границы: PostgreSQL, известная схема, чтение без изменений, конкретный параметр и требование объяснить пустой случай. Последняя фраза отделяет написание команды от её выполнения. Это особенно полезно, если Claude Code подключён к базе и умеет запускать команды самостоятельно.

Перед выполнением проверь ответ по чек-листу:

  • используются реальные имена таблиц и столбцов;
  • тип операции соответствует задаче: для чтения начинается с SELECT;
  • связь записана как games.player_id = players.id;
  • пользовательское значение передаётся параметром, а не склейкой строк;
  • WHERE ограничивает изменение, если запрос содержит UPDATE или DELETE;
  • порядок сортировки явно указан, если важны лучшие или последние записи;
  • ИИ объяснил случай без найденных строк;
  • запрос предназначен для той системы базы данных, которая используется в проекте;
  • перед выполнением понятны ожидаемые столбцы и количество строк;
  • настоящие секреты и личные данные не попали в переписку.

Если ответ непонятен, не проси просто «исправить». Укажи место сомнения: «Объясни, почему в GROUP BY указаны оба столбца», «Покажи безопасный SELECT для проверки того же WHERE», «Может ли этот запрос изменить больше одной строки?». Хороший следующий вопрос проверяет одно конкретное решение.

Для изменяющего запроса полезна более строгая формулировка:

💡

Нужно обновить score только у игрока с переданным id. Сначала дай проверочный SELECT с тем же условием, затем параметризованный UPDATE. Оберни изменение в транзакцию, не выполняй COMMIT, укажи ожидаемое число изменённых строк. Если строка не одна, операция должна считаться ошибкой.

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

Сравнивай запрос не с тем, насколько убедительно звучит объяснение, а с исходной задачей. Если нужно показать рейтинг, там должны быть сортировка и ограничение. Если нужно изменить одного игрока, должно быть условие по уникальному идентификатору. Если данные принадлежат конкретному пользователю, одного SQL может быть недостаточно — проверь права и RLS.

Как применить и проверить SQL-запрос без риска

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

Шаг 1. Скажи, какой результат ожидаешь

До запуска запиши обычными словами: «получить не больше десяти строк; сначала самый большой score; при равенстве раньше созданная запись выше». Такая формулировка позволяет заметить, что в запросе не хватает ORDER BY или второго правила сортировки.

Шаг 2. Определи тип операции

Посмотри на первое ключевое слово. SELECT читает, INSERT добавляет, UPDATE изменяет, DELETE удаляет. Если задача была только посмотреть данные, а ответ ИИ содержит изменяющую команду, не запускай её.

Шаг 3. Проверь таблицы и столбцы

Сверь имена со схемой. Отдельно проверь одинаковые столбцы в разных таблицах: players.score может быть рекордом, а games.score — результатом партии. Псевдонимы p.score и g.score помогают не перепутать их.

Шаг 4. Проверь область действия

Для UPDATE и DELETE найди WHERE и выполни безопасный SELECT с тем же условием. Ожидаешь одну строку — результат проверки тоже должен содержать одну. Ноль или несколько строк означает, что условие нужно пересмотреть.

Шаг 5. Используй транзакцию и резервную копию

Если операция важная, заранее проверь возможность восстановления. Транзакция позволяет отменить неподтверждённое изменение, а резервная копия нужна после подтверждения или при более серьёзной ошибке. Это разные средства защиты.

Шаг 6. Проверь результат отдельным SELECT

После выполнения снова прочитай изменённую строку. Не ограничивайся сообщением «успешно»: оно подтверждает выполнение команды, но не её соответствие твоему намерению.

Для тренировки можно пройти короткую последовательность:

SELECT id, name, score
FROM players
ORDER BY id;

INSERT INTO players (name, score)
VALUES ('Тестовый игрок', 50);

SELECT id, name, score
FROM players
WHERE name = 'Тестовый игрок';

Дальше возьми полученный id, обнови именно эту строку, проверь её и только затем удали по тому же идентификатору. Не выполняй пример в рабочей базе: имя не гарантирует уникальность, а тренировочная запись не должна смешиваться с настоящими данными.

Реляционная модель — не единственный способ хранения. В документоориентированных системах записи и запросы устроены иначе; сравнение есть в объяснении MongoDB и NoSQL-баз данных. Для проекта на PostgreSQL всё равно ориентируйся на таблицы, ключи, связи и SQL.

Частые вопросы

Нужно ли учить SQL, если Claude Code пишет запросы?

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

Какой SQL-запрос показывает все записи таблицы?

SELECT * FROM players; вернёт все строки и столбцы players. В рабочем приложении лучше перечислить нужные столбцы и при большой таблице добавить осмысленные фильтр, сортировку и ограничение.

Почему SELECT иногда возвращает строки в разном порядке?

Без ORDER BY база не обещает определённую последовательность. Если порядок важен, задай его явно и добавь второе уникальное или стабильное правило для одинаковых значений.

Можно ли отменить DELETE или UPDATE?

Можно отменить изменение внутри незавершённой транзакции командой ROLLBACK. После COMMIT восстановление зависит от резервных копий и возможностей сервиса, поэтому сначала проверяй тот же WHERE через SELECT.

Чем SQL отличается от PostgreSQL?

SQL — язык запросов, а PostgreSQL — система управления базой данных, которая этот язык понимает и расширяет. Другие реляционные базы тоже используют SQL, но отдельные функции и детали синтаксиса могут различаться.

Защищают ли параметры от всех ошибок в запросе?

Нет. Они защищают от превращения пользовательского значения в часть SQL-команды, но не исправляют неверный WHERE, лишние права или ошибочную бизнес-логику. Нужны также проверка результата, ограничения доступа, транзакции и резервные копии.

SQL становится понятным, если читать его не как код из загадочных слов, а как точную инструкцию для таблицы. Сначала найди действие, затем источник данных, условие, порядок и ограничение. Пусть Claude Code пишет синтаксис, а ты сохраняй контроль над тем, что произойдёт с данными после запуска.

Читай дальше

Все статьи

Не просто статьи — тебя доведут до результата

В практикуме за 2499 ₽ рядом живая команда практикующих разработчиков и маркетологов: ведём по шагам до твоего работающего приложения. Не «ролики и сам разбирайся» — помогаем на каждом затыке.

Перейти к практикуму
Все статьи Ещё: технологии и архитектура