Лучшие практики SQL: читаемые и поддерживаемые запросы
Урок 10.1 · Время чтения: ~8 мин
Лучшие практики SQL - это привычки форматирования и именования, благодаря которым запрос легко читать, проверять и менять. В этом уроке вы узнаете, как располагать предложения и столбцы, как называть таблицы, столбцы и псевдонимы, когда помогают комментарии, какие привычки предотвращают ошибки и как превратить запрос в одну строку в код, который команда сможет поддерживать.
В предыдущем модуле вы работали с представлениями и временными таблицами, и запросы выросли до множества соединений и нескольких шагов. При таком размере качество кода важно не меньше, чем правильность: SQL читают гораздо чаще, чем пишут, - коллеги на ревью и вы сами через несколько недель.
Хорошо структурированный код делает ошибки заметными, ускоряет ревью и упрощает доработку запросов. Ни одно из этих правил не меняет того, что делает база данных; они меняют то, как быстро человек понимает запрос.
Зачем нужен стиль кода
Запрос из 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), сделайте отступы в списках столбцов, замените непонятные псевдонимы, дайте имена вычисляемым столбцам и добавьте комментарий там, где логика требует пояснения. Запустите старую и новую версии и убедитесь, что результаты совпадают.