🚀 Начался ноябрь! SQLtest.online остаётся бесплатным благодаря донатам таких людей, как вы. Помогите собрать сумму этого месяца пораньше, чтобы сайт работал без перебоев. Поддержать сейчас →
SQL код скопирован в буфер обмена

Лучшие практики SQL: читаемые и поддерживаемые запросы

Урок 10.1 · Время чтения: ~8 мин

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

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

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

Качество SQL-кода: SELECT, отформатированный по одному столбцу на строку, имена в snake_case вместо camelCase и комментарий, объясняющий, зачем нужен фильтр

Зачем нужен стиль кода

Запрос из 5–10 строк понятен с первого взгляда. Отчёт с множеством JOIN, подзапросов или CTE - нет, и единый стиль помогает:

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

Форматирование и отступы

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

Ключевые слова в верхнем регистре

Пишите ключевые слова SQL (SELECT, FROM, WHERE, JOIN, GROUP BY) в верхнем регистре, а имена - в нижнем. Тогда структура запроса выделяется на фоне данных, с которыми он работает.

-- Трудно читать
select title, rental_rate from film where rating = 'G';

-- Легче читать
SELECT title, rental_rate
FROM film
WHERE rating = 'G';

Результат: оба запроса возвращают одни и те же 178 фильмов; отличается только раскладка.

Одно предложение на строку

Начинайте каждое основное предложение с новой строки. Если в SELECT или GROUP BY много столбцов, ставьте каждый на отдельную строку с отступом:

SELECT
    customer_id,
    first_name,
    last_name,
    email
FROM customer
WHERE active = 1
ORDER BY last_name;

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


Соглашения об именовании

Нижний регистр и snake_case

Фактический стандарт - имена в нижнем регистре с подчёркиваниями между словами: customer_orders, total_amount, а не CustomerOrders или TotalAmount. СУБД по-разному обращаются с регистром идентификаторов: PostgreSQL приводит имена без кавычек к нижнему регистру, а MySQL в Linux различает регистр в именах таблиц. snake_case избавляет от этих сюрпризов.

Единые префиксы

Некоторые команды обозначают типы объектов префиксами, например v_ для представлений (v_active_customers) и tmp_ для временных таблиц (tmp_monthly_report). В Sakila префиксов нет: её представления называются customer_list, film_list, sales_by_film_category. Подходят оба варианта; важно выбрать одно правило для всей базы.

SELECT category, total_sales
FROM sales_by_film_category
ORDER BY total_sales DESC
LIMIT 3;

Результат: три категории с наибольшими продажами: Sports, Sci-Fi и Animation.


Понятные псевдонимы

Псевдонимы таблиц

Используйте короткие, но осмысленные псевдонимы, например первые буквы имени таблицы. Псевдонимы вроде t1, t2, a, b заставляют читателя выяснять, что означает каждый:

-- Непонятно: что такое t1 и t2?
SELECT t1.first_name, t2.amount
FROM customer t1
JOIN payment t2 ON t1.customer_id = t2.customer_id;

-- Понятно
SELECT c.first_name, p.amount
FROM customer c
JOIN payment p ON c.customer_id = p.customer_id;

Имена вычисляемых столбцов

Давайте имя каждому агрегату и выражению с помощью AS. Столбец отчёта с названием COUNT(*) неудобно использовать в приложении или таблице Excel:

SELECT
    c.name AS category_name,
    COUNT(*) AS film_count,
    AVG(f.replacement_cost) AS avg_replacement_cost
FROM film f
JOIN film_category fc ON f.film_id = fc.film_id
JOIN category c ON fc.category_id = c.category_id
GROUP BY c.name
ORDER BY film_count DESC;

Результат: 16 категорий с понятными именами столбцов; больше всего фильмов в Sports - 74.


Комментарии

Комментарии объясняют, зачем код что-то делает, когда причина не видна из самого кода: бизнес-правило, необычный фильтр, известная проблема в данных.

  • Однострочные комментарии (--) поясняют фильтр или формулу.
  • Блочные комментарии (/* ... */) описывают назначение скрипта.
/*
  Отчёт: траты клиентов в июле 2005 года
  Используется: ежемесячная маркетинговая рассылка
*/
SELECT
    customer_id,
    SUM(amount) AS july_spent
FROM payment
WHERE payment_date >= '2005-07-01'  -- весь месяц,
  AND payment_date < '2005-08-01'   -- включая 31 июля после полуночи
GROUP BY customer_id
ORDER BY july_spent DESC;

Результат: 599 клиентов с их тратами за июль 2005 года.

Не комментируйте то, что уже говорит код: -- выбираем customer id над SELECT customer_id только добавляет шум, а устаревшие комментарии хуже, чем никаких.


Привычки, которые делают запросы безопаснее

  • Перечисляйте нужные столбцы вместо SELECT * в отчётах, представлениях и коде приложений. Тогда новый столбец в таблице не изменит и не замедлит ваш результат.
  • Используйте явный JOIN ... ON вместо перечисления таблиц через запятую во FROM. Условие соединения остаётся рядом с таблицей, и его труднее забыть.
  • Предпочитайте полуоткрытые диапазоны дат (>= start AND < end) вместо BETWEEN для столбцов с датой и временем, как в примере выше.
  • Договоритесь о стиле и автоматизируйте его. Форматтеры и линтеры, например SQLFluff, применяют правила за вас, и на ревью не спорят о пробелах.

Практический пример: приводим в порядок запутанный запрос

До: всё в одну строку, у вычисляемых столбцов нет имён:

select f.title,c.name,count(r.rental_id) from film f join film_category fc on f.film_id=fc.film_id join category c on fc.category_id=c.category_id join inventory i on f.film_id=i.film_id join rental r on i.inventory_id=r.inventory_id group by f.title,c.name having count(r.rental_id)>30 order by count(r.rental_id) desc;

После: одно предложение на строку, выровненные соединения, именованные столбцы:

SELECT
    f.title,
    c.name AS category_name,
    COUNT(r.rental_id) AS rental_count
FROM film f
JOIN film_category fc ON f.film_id = fc.film_id
JOIN category c       ON fc.category_id = c.category_id
JOIN inventory i      ON f.film_id = i.film_id
JOIN rental r         ON i.inventory_id = r.inventory_id
GROUP BY f.title, c.name
HAVING COUNT(r.rental_id) > 30
ORDER BY rental_count DESC;

Результат: обе версии возвращают одни и те же 16 фильмов, которые брали в аренду больше 30 раз; первым идёт BUCKET BROTHERHOOD с 34 арендами.

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


Ключевые выводы:

  • Пишите ключевые слова в верхнем регистре, а имена - в нижнем, в snake_case.
  • Начинайте каждое предложение с новой строки, а длинные списки столбцов пишите по одному на строку.
  • Используйте осмысленные псевдонимы таблиц и называйте каждый вычисляемый столбец через AS.
  • Комментируйте «зачем», а не «что»: бизнес-правила и неочевидные фильтры.
  • Перечисляйте столбцы вместо SELECT *, используйте явный JOIN ... ON и полуоткрытые диапазоны дат.
  • Договоритесь в команде об одном стиле и применяйте его форматтером или линтером.

Часто задаваемые вопросы

Обязательно ли писать ключевые слова SQL в верхнем регистре?

Нет. СУБД принимают ключевые слова в любом регистре. Верхний регистр - соглашение, которое делает структуру длинных запросов заметнее; важнее всего, чтобы вся команда придерживалась одного стиля.

Что делает SQL-код поддерживаемым?

Единое форматирование, понятные имена и псевдонимы, именованные вычисляемые столбцы и короткие комментарии там, где логика неочевидна. Такой код проще проверять, менять и отлаживать, в том числе тем, кто его не писал.

Почему стоит избегать SELECT * в рабочих запросах?

SELECT * возвращает все столбцы, включая добавленные в таблицу позже. Это может сломать приложение, которое ждёт фиксированный набор столбцов, передать больше данных, чем нужно, и помешать использованию покрывающих индексов. Перечисляйте нужные столбцы.

Как привести в порядок запутанный SQL-запрос?

Разбейте его на предложения на отдельных строках (SELECT, FROM, JOIN, WHERE, GROUP BY, ORDER BY), сделайте отступы в списках столбцов, замените непонятные псевдонимы, дайте имена вычисляемым столбцам и добавьте комментарий там, где логика требует пояснения. Запустите старую и новую версии и убедитесь, что результаты совпадают.