Всем привет! Я - практикующий исследователь данных, и на этом канале делюсь тем, что реально работает в IT. Никакой сухой теории, только личный опыт, рабочие инструменты и грабли, на которые я уже наступил за вас. Рад, что вы здесь!🧸
Когда я только начинал в аналитике, думал что SQL это просто SELECT и WHERE. Потом на первой работе понял, что этого недостаточно. За почти 4 года работы собрал список того, что реально нужно знать (во всяком случае я сталкивался и работал с этим вещами не раз). Многие вещи я учил по мере необходимости, когда сталкивался с конкретными задачами.
Вот полный чек-лист навыков SQL для аналитика данных в 2026 году. Разбил по уровням сложности и добавил примеры из реальной работы.
Базовый уровень (обязательно)
Это минимум который нужен для работы аналитиком. Без этого не обойтись. На изучение базового уровня у меня ушло около месяца ежедневной практики (это навскидку, так как всё таки SQL я изучал в рамках разработки на Python, но от этого суть не меняется).
SELECT и фильтрация
SELECT, FROM, WHERE - основа основ. Без этого ничего не сделаешь.
Пример из работы:
SELECT product_name, price, quantity
FROM sales
WHERE price > 1000;
Подобные запросы я использую каждый день по несколько раз в день (на самом деле больше). Выбираю нужные колонки, указываю таблицу, фильтрую по условию.
Операторы сравнения (=, <>, >, <, >=, <=) - для фильтрации данных.
Часто используется:
= для точного совпадения
<> или != для исключения
> и < для диапазонов
>= и <= для включения границ
Логические операторы (AND, OR, NOT) - для сложных условий.
Пример:
SELECT * FROM sales
WHERE price > 1000 AND category = 'Electronics'
OR price > 5000;
Важно понимать приоритет операторов. AND выполняется раньше OR. Лучше использовать скобки для ясности.
IN, NOT IN - для проверки принадлежности списку.
Использую когда нужно найти несколько значений:
SELECT * FROM sales
WHERE category IN ('Electronics', 'Books', 'Clothing');
Гораздо удобнее чем несколько OR условий.
LIKE - для поиска по шаблону. Полезно для текстовых полей.
Примеры:
LIKE 'A%' - начинается с A
LIKE '%test%' - содержит test
LIKE '_test' - заканчивается на test (один символ перед)
Использую для поиска по названиям, описаниям, когда точное значение неизвестно.
BETWEEN - для диапазонов. Удобнее чем два условия с AND.
SELECT * FROM sales
WHERE price BETWEEN 1000 AND 5000;
Эквивалентно price >= 1000 AND price <= 5000, но читается лучше.
Сортировка и ограничение
ORDER BY - для сортировки результатов.
Использую постоянно:
SELECT * FROM sales
ORDER BY price DESC, date ASC;
DESC для убывания, ASC для возрастания (по умолчанию).
LIMIT / TOP - для ограничения количества строк.
В PostgreSQL и MySQL это LIMIT:
SELECT * FROM sales
ORDER BY price DESC
LIMIT 10;
В SQL Server это TOP:
SELECT TOP 10 * FROM sales
ORDER BY price DESC;
Использую для топ-10, топ-100 и так далее.
OFFSET - для пагинации. Пропускает указанное количество строк.
SELECT * FROM sales
ORDER BY date DESC
LIMIT 10 OFFSET 20;
Показывает строки 21-30. Полезно для постраничной навигации.
Агрегация
COUNT, SUM, AVG, MIN, MAX - основные агрегатные функции.
Использую каждый день:
SELECT
COUNT(*) as total_orders,
SUM(price) as total_revenue,
AVG(price) as avg_price,
MIN(price) as min_price,
MAX(price) as max_price
FROM sales;
COUNT(*) считает все строки, COUNT(column) считает непустые значения.
GROUP BY - для группировки данных.
Без этого агрегация не работает правильно:
SELECT category, COUNT(*) as count, SUM(price) as total
FROM sales
GROUP BY category;
Показывает количество и сумму по каждой категории.
Важно: все колонки в SELECT должны быть либо в GROUP BY, либо агрегатными функциями.
HAVING - для фильтрации групп. Работает после GROUP BY.
SELECT category, COUNT(*) as count
FROM sales
GROUP BY category
HAVING COUNT(*) > 10;
Показывает только категории с более чем 10 продажами. WHERE фильтрует строки до группировки, HAVING после.
DISTINCT - для уникальных значений.
SELECT DISTINCT category FROM sales;
Показывает уникальные категории. Полезно когда нужно список без дубликатов.
Средний уровень (нужно знать)
Это то что нужно для большинства задач аналитика. Изучал постепенно, по мере необходимости.
JOIN операции
INNER JOIN (он же просто JOIN) - только совпадающие строки из обеих таблиц.
Самый частый JOIN в работе:
SELECT s.product_name, s.price, c.category_name
FROM sales s
JOIN categories c ON s.category_id = c.id;
Показывает только продажи у которых есть категория. Если категории нет, строка не попадет в результат.
LEFT JOIN - все строки из левой таблицы, даже если нет совпадения.
Использую иногда даже чаще чем JOIN:
SELECT s.product_name, s.price, c.category_name
FROM sales s
LEFT JOIN categories c ON s.category_id = c.id;
Показывает все продажи, даже если категория не найдена (будет NULL). Это важно для анализа - не теряем данные.
RIGHT JOIN - все строки из правой таблицы. Использую редко, обычно можно переписать на LEFT JOIN.
FULL OUTER JOIN - все строки из обеих таблиц. Использую очень редко, в основном для сравнения таблиц.
Сложные JOIN - несколько таблиц одновременно.
Пример из реальной задачи:
SELECT
s.product_name,
s.price,
c.category_name,
u.user_name
FROM sales s
LEFT JOIN categories c ON s.category_id = c.id
LEFT JOIN users u ON s.user_id = u.id;
Объединяю три таблицы. Важно понимать порядок JOIN - он влияет на результат.
Подзапросы
Подзапросы в WHERE - для сложных условий.
Пример:
SELECT * FROM sales
WHERE price > (SELECT AVG(price) FROM sales);
Находит продажи выше среднего. Подзапрос выполняется для каждой строки, может быть медленным на больших данных.
Подзапросы в SELECT - для вычислений.
SELECT
product_name,
price,
(SELECT AVG(price) FROM sales) as avg_price,
price - (SELECT AVG(price) FROM sales) as difference
FROM sales;
Сравниваю цену со средним. Удобно, но может быть медленно.
EXISTS / NOT EXISTS - проверка существования.
SELECT * FROM products p
WHERE EXISTS (
SELECT 1 FROM sales s
WHERE s.product_id = p.id
);
Находит продукты которые были проданы. EXISTS быстрее чем IN на больших данных.
IN с подзапросами - для фильтрации по списку.
SELECT * FROM sales
WHERE category_id IN (
SELECT id FROM categories
WHERE active = true
);
Находит продажи активных категорий. Удобно когда список динамический.
Оконные функции
Это мощный инструмент который я освоил не сразу. Но теперь использую постоянно.
ROW_NUMBER() - нумерация строк.
SELECT
product_name,
price,
ROW_NUMBER() OVER (ORDER BY price DESC) as rank
FROM sales;
Нумерует строки по цене. Полезно для ранжирования.
RANK(), DENSE_RANK() - ранжирование с учетом одинаковых значений.
RANK пропускает номера при одинаковых значениях, DENSE_RANK нет:
SELECT
product_name,
price,
RANK() OVER (ORDER BY price DESC) as rank,
DENSE_RANK() OVER (ORDER BY price DESC) as dense_rank
FROM sales;
Использую для топ-10, топ-100 с учетом одинаковых значений.
SUM() OVER(), AVG() OVER() - скользящие суммы и средние.
SELECT
date,
price,
SUM(price) OVER (ORDER BY date) as running_total,
AVG(price) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as week_avg
FROM sales;
Показывает накопительную сумму и среднее за неделю. Очень полезно для временных рядов.
LAG(), LEAD() - предыдущее и следующее значение.
SELECT
date,
price,
LAG(price) OVER (ORDER BY date) as prev_price,
LEAD(price) OVER (ORDER BY date) as next_price
FROM sales;
Сравниваю с предыдущим и следующим значением. Использую для анализа изменений.
PARTITION BY - группировка в оконных функциях.
SELECT
category,
product_name,
price,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) as rank_in_category
FROM sales;
Ранжирую внутри каждой категории. Мощная вещь для анализа по группам.
Работа с датами
Даты это постоянная головная боль в аналитике. Разные форматы, временные зоны, вычисления.
DATE_FORMAT / TO_CHAR - форматирование дат.
В MySQL:
SELECT DATE_FORMAT(date, '%Y-%m-%d') as formatted_date
FROM sales;
В PostgreSQL:
SELECT TO_CHAR(date, 'YYYY-MM-DD') as formatted_date
FROM sales;
Использую для группировки по периодам, форматирования для отчетов.
DATE_ADD, DATE_SUB - добавление и вычитание времени.
SELECT
date,
DATE_ADD(date, INTERVAL 1 DAY) as next_day,
DATE_SUB(date, INTERVAL 1 MONTH) as prev_month
FROM sales;
Вычисляю даты относительно текущей. Полезно для периодов сравнения.
DATEDIFF - разница между датами.
SELECT
order_date,
delivery_date,
DATEDIFF(delivery_date, order_date) as days_to_delivery
FROM orders;
Считаю сколько дней между датами. Использую для анализа сроков.
EXTRACT - извлечение частей даты.
SELECT
date,
EXTRACT(YEAR FROM date) as year,
EXTRACT(MONTH FROM date) as month,
EXTRACT(DAY FROM date) as day
FROM sales;
Разбиваю дату на части. Использую для группировки по годам, месяцам, дням.
Временные зоны - сложная тема. В PostgreSQL есть TIMEZONE функции, в MySQL сложнее.
Обычно работаю с UTC в базе, конвертирую в нужную зону при выводе.
Строковые функции
CONCAT - объединение строк.
SELECT CONCAT(first_name, ' ', last_name) as full_name
FROM users;
Объединяю несколько полей. В PostgreSQL можно использовать ||, в MySQL CONCAT.
SUBSTRING - извлечение части строки.
SELECT SUBSTRING(email, 1, 5) as email_start
FROM users;
Беру первые 5 символов. Использую для маскирования данных, извлечения префиксов.
REPLACE - замена подстроки.
SELECT REPLACE(phone, '-', '') as clean_phone
FROM users;
Убираю дефисы из телефона. Полезно для очистки данных.
UPPER, LOWER - изменение регистра.
SELECT UPPER(name) as upper_name, LOWER(name) as lower_name
FROM products;
Привожу к одному регистру для сравнения. Важно для поиска без учета регистра.
TRIM - удаление пробелов.
SELECT TRIM(name) as clean_name
FROM products;
Убираю лишние пробелы в начале и конце. Частая проблема в данных.
LENGTH - длина строки.
SELECT name, LENGTH(name) as name_length
FROM products;
Проверяю длину. Использую для валидации данных, поиска аномалий.
Продвинутый уровень (желательно)
Это то что отличает опытного аналитика от начинающего. Изучал когда базового уровня стало недостаточно.
CTE (Common Table Expressions)
WITH - создание временных таблиц в запросе.
Использую для читаемости сложных запросов:
WITH sales_by_category AS (
SELECT category, SUM(price) as total
FROM sales
GROUP BY category
),
top_categories AS (
SELECT category
FROM sales_by_category
ORDER BY total DESC
LIMIT 5
)
SELECT * FROM top_categories;
Разбиваю сложный запрос на части. Легче читать и отлаживать.
Рекурсивные CTE - для иерархических данных.
Использую редко, но когда нужно - незаменимо:
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id, 1 as level
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, ct.level + 1
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree;
Строю дерево категорий. Сложно, но мощно.
Оптимизация запросов
Понимание индексов - важно для производительности.
Индексы ускоряют поиск, но замедляют вставку. Нужно балансировать.
Проверяю какие индексы есть:
SHOW INDEX FROM sales;
Создаю индекс если нужно:
CREATE INDEX idx_category ON sales(category_id);
EXPLAIN PLAN - план выполнения запроса.
Показывает как база выполняет запрос:
EXPLAIN SELECT * FROM sales WHERE category_id = 1;
Смотрю на стоимость операций, использование индексов. Помогает найти узкие места.
Избежание N+1 проблем - один запрос вместо многих.
Вместо:
-- Плохо: много запросов
SELECT * FROM users;
-- Потом для каждого пользователя:
SELECT * FROM orders WHERE user_id = ?
Делаю:
-- Хорошо: один запрос с JOIN
SELECT u.*, o.*
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
Один запрос вместо сотен. Значительно быстрее.
Оптимизация JOIN - порядок и условия важны.
JOIN с индексированными полями быстрее. Меньше таблиц в JOIN - быстрее запрос.
Работа с NULL
NULL это отдельная тема. Много подводных камней.
COALESCE - первое не NULL значение.
SELECT COALESCE(middle_name, '') as middle
FROM users;
Если middle_name NULL, вернет пустую строку. Удобно для обработки пропусков.
NULLIF - возвращает NULL если значения равны.
SELECT NULLIF(price, 0) as price
FROM sales;
Если цена 0, вернет NULL. Полезно для фильтрации нулевых значений.
IS NULL / IS NOT NULL - проверка на NULL.
SELECT * FROM sales
WHERE discount IS NULL;
Важно использовать IS NULL, а не = NULL. = NULL всегда возвращает NULL (не true, не false).
Обработка NULL в агрегации - COUNT ведет себя по-особому.
COUNT(*) считает все строки включая NULL. COUNT(column) считает только не NULL значения.
SUM, AVG игнорируют NULL автоматически.
Условная логика
CASE WHEN - условная логика в SQL.
Использую постоянно:
SELECT
product_name,
price,
CASE
WHEN price > 1000 THEN 'Expensive'
WHEN price > 500 THEN 'Medium'
ELSE 'Cheap'
END as price_category
FROM sales;
Классифицирую данные по условиям. Мощный инструмент.
IF (в MySQL) - простая условная логика.
SELECT IF(price > 1000, 'Expensive', 'Cheap') as category
FROM sales;
Проще чем CASE для простых условий.
IIF (в SQL Server) - аналогично IF.
SELECT IIF(price > 1000, 'Expensive', 'Cheap') as category
FROM sales;
Что не нужно знать (пока)
Это то что изучать не обязательно для аналитика. Оставьте разработчикам.
- Создание таблиц и схем - это для разработчиков. Аналитик работает с существующими таблицами.
- Триггеры и хранимые процедуры - редко нужны аналитику. Обычно достаточно запросов.
- Сложная оптимизация БД - это для администраторов. Аналитику достаточно понимать основы.
- Администрирование баз данных - не ваша задача. Фокусируйтесь на анализе.
Практические советы из опыта
- Начните с базового уровня. Это 80% работы аналитика. Освойте SELECT, WHERE, JOIN, GROUP BY - этого хватит для большинства задач.
- Оконные функции решают много задач. Изучите их после базового уровня. ROW_NUMBER(), SUM() OVER(), LAG() - это то что отличает опытного аналитика.
- Практикуйтесь на реальных данных. Kaggle, SQLZoo, LeetCode - отличные ресурсы. Но лучше всего работа с реальными данными вашей компании.
- Учите синтаксис вашей БД. PostgreSQL, MySQL, SQL Server, ClickHouse имеют различия. Не пытайтесь выучить все сразу, фокусируйтесь на том что используете.
- Используйте CTE для читаемости. Сложные запросы легче понимать с WITH. Разбивайте на части, комментируйте.
- Проверяйте результаты. Всегда смотрите на первые строки результата. COUNT(*) перед агрегацией. Проверяйте логику.
- Документируйте сложные запросы. Через месяц не вспомните что делал. Комментируйте, объясняйте логику.
- Используйте версионный контроль. Сохраняйте запросы в Git. Отслеживайте изменения. Полезно для команды.
Типичные задачи и решения
Топ-10 товаров по продажам:
SELECT product_name, SUM(price) as total_sales
FROM sales
GROUP BY product_name
ORDER BY total_sales DESC
LIMIT 10;
Сравнение с предыдущим периодом:
WITH monthly_sales AS (
SELECT
DATE_TRUNC('month', date) as month,
SUM(price) as sales
FROM sales
GROUP BY DATE_TRUNC('month', date)
)
SELECT
month,
sales,
LAG(sales) OVER (ORDER BY month) as prev_month_sales,
sales - LAG(sales) OVER (ORDER BY month) as difference
FROM monthly_sales;
Фильтрация и агрегация:
SELECT
category,
COUNT(*) as count,
AVG(price) as avg_price
FROM sales
WHERE date >= '2025-01-01'
GROUP BY category
HAVING COUNT(*) > 10
ORDER BY avg_price DESC;
Что делать дальше
Если вы только начинаете, сфокусируйтесь на базовом и среднем уровне. Этого хватит для 90% задач аналитика. Остальное учите по мере необходимости.
Для практики рекомендую:
- SQLZoo для базовых навыков - интерактивные упражнения, пошаговое обучение
- LeetCode для сложных задач - подготовка к собеседованиям, продвинутые техники
- Собственные проекты на реальных данных - лучший способ закрепить знания
- Stepik - естественно, много бесплатных и очень хороших курсов.
Помните: SQL это инструмент. Важнее понимать что искать в данных, чем знать все функции наизусть. Начинайте с простого, усложняйте постепенно.
Я не претендую на последнюю инстанцию, я пишу о своём пути и опыте. Спасибо что дочитали до конца. Подпишитесь👇👇👇, ставьте лайки 👍🏽👍🏽👍🏽 впереди много интересных статей про навыки, инструменты, обучение, лайфхаки и пути аналитика.