SQL для начинающих: 10 запросов, которые нужно знать каждому аналитику

SQL для начинающих: 10 запросов, которые нужно знать каждому аналитику

Введение

SQL (Structured Query Language) — это язык структурированных запросов, который является стандартным инструментом для работы с реляционными базами данных. Для аналитика владение SQL — это не просто полезный навык, а необходимый инструмент ежедневной работы, позволяющий извлекать, анализировать и трансформировать данные без зависимости от разработчиков или специализированных BI-инструментов.

В современном мире данных SQL остается одним из самых востребованных навыков. По статистике рынка труда, более 80% вакансий для аналитиков требуют знания SQL. Это объясняется универсальностью языка — он работает практически со всеми популярными системами управления базами данных: PostgreSQL, MySQL, Oracle, SQL Server, SQLite и многими другими.

Основы реляционных баз данных

Ключевые понятия

Прежде чем погрузиться в изучение запросов, важно понимать основную терминологию:

ТерминОпределениеАналогия с Excel
База данных (Database)Организованная коллекция данныхФайл Excel
Таблица (Table)Структурированный набор данных одного типаЛист в Excel
Строка (Row/Record)Одна запись в таблицеСтрока в Excel
Столбец (Column)Атрибут или поле данныхСтолбец в Excel
Первичный ключ (Primary Key)Уникальный идентификатор строкиУникальный ID строки
Внешний ключ (Foreign Key)Связь между таблицамиСсылка на другой лист
Индекс (Index)Структура для ускорения поискаОглавление книги

Структура данных для примеров

Для демонстрации SQL-запросов будем использовать базу данных интернет-магазина со следующими таблицами:

Таблица: customers (клиенты)

customer_idfirst_namelast_nameemailcityregistration_date
1ИванПетровivan@mail.ruМосква2023-01-15
2МарияСидороваmaria@gmail.comСанкт-Петербург2023-02-20
3АлексейИвановalex@yandex.ruМосква2023-03-10

Таблица: orders (заказы)

order_idcustomer_idorder_datetotal_amountstatus
10112023-06-015000.00completed
10222023-06-023500.00completed
10312023-06-032000.00cancelled

Таблица: products (товары)

product_idproduct_namecategorypricestock_quantity
1НоутбукЭлектроника45000.0010
2МышьАксессуары1500.0050
3КлавиатураАксессуары3000.0030

Таблица: order_items (позиции заказов)

order_item_idorder_idproduct_idquantityprice
11011145000.00
2101221500.00
3102313000.00

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

1. SELECT — Выборка данных

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

Базовый синтаксис:

sql

-- Выбор всех столбцов SELECT * FROM customers;  -- Выбор конкретных столбцов SELECT first_name, last_name, email FROM customers;  -- Выбор с псевдонимами SELECT    first_name AS Имя,     last_name AS Фамилия,     email AS "Электронная почта" FROM customers;

Практические примеры:

sql

-- Получить список всех товаров с ценой и остатками SELECT    product_name,     price,     stock_quantity FROM products;  -- Вывести информацию о заказах с форматированием SELECT    order_id AS "Номер заказа",     order_date AS "Дата заказа",     total_amount AS "Сумма",     status AS "Статус" FROM orders;

Работа с вычисляемыми полями:

sql

-- Вычисление общей стоимости товаров на складе SELECT    product_name,     price,     stock_quantity,     price * stock_quantity AS total_value FROM products;  -- Конкатенация строк SELECT    first_name || ' ' || last_name AS full_name,     email,     city FROM customers;

2. WHERE — Фильтрация данных

WHERE позволяет фильтровать строки по определенным условиям. Это критически важный инструмент для работы с большими объемами данных.

Операторы сравнения:

ОператорОписаниеПример использования
=РавноWHERE city = 'Москва'
!= или <>Не равноWHERE status != 'cancelled'
>БольшеWHERE price > 1000
<МеньшеWHERE quantity < 10
>=Больше или равноWHERE order_date >= '2023-01-01'
<=Меньше или равноWHERE total_amount <= 5000
BETWEENВ диапазонеWHERE price BETWEEN 1000 AND 5000
INВ списке значенийWHERE city IN ('Москва', 'Санкт-Петербург')
LIKEПоиск по шаблонуWHERE email LIKE '%@gmail.com'
IS NULLПустое значениеWHERE phone IS NULL

Примеры использования:

sql

-- Клиенты из Москвы SELECT * FROM customers WHERE city = 'Москва';  -- Заказы на сумму от 3000 до 10000 рублей SELECT * FROM orders WHERE total_amount BETWEEN 3000 AND 10000;  -- Товары категории "Электроника" с ценой выше 10000 SELECT product_name, price FROM products WHERE category = 'Электроника'  AND price > 10000;  -- Клиенты с email на gmail или yandex SELECT first_name, last_name, email FROM customers WHERE email LIKE '%@gmail.com'   OR email LIKE '%@yandex.ru';

Логические операторы:

sql

-- Комбинация условий с AND SELECT * FROM orders WHERE order_date >= '2023-06-01'   AND order_date <= '2023-06-30'   AND status = 'completed';  -- Использование OR и скобок для группировки SELECT * FROM products WHERE (category = 'Электроника' OR category = 'Аксессуары')   AND price < 5000;  -- Отрицание с NOT SELECT * FROM customers WHERE NOT city = 'Москва';

3. JOIN — Объединение таблиц

JOIN — это мощный инструмент для объединения данных из нескольких таблиц. Понимание различных типов JOIN критически важно для аналитика.

Типы JOIN:

Тип JOINОписаниеКогда использовать
INNER JOINВозвращает только совпадающие записи из обеих таблицКогда нужны только связанные данные
LEFT JOINВсе записи из левой таблицы + совпадения из правойКогда нужны все записи основной таблицы
RIGHT JOINВсе записи из правой таблицы + совпадения из левойРедко используется, можно заменить LEFT JOIN
FULL OUTER JOINВсе записи из обеих таблицКогда нужны все данные независимо от связей
CROSS JOINДекартово произведение таблицДля генерации всех возможных комбинаций

INNER JOIN — Внутреннее объединение:

sql

-- Получить информацию о заказах с данными клиентов SELECT    o.order_id,     o.order_date,     o.total_amount,     c.first_name,     c.last_name,     c.email FROM orders o INNER JOIN customers c ON o.customer_id = c.customer_id;  -- Детали заказов с информацией о товарах SELECT    oi.order_id,     p.product_name,     p.category,     oi.quantity,     oi.price,     oi.quantity * oi.price AS item_total FROM order_items oi INNER JOIN products p ON oi.product_id = p.product_id;

LEFT JOIN — Левое объединение:

sql

-- Все клиенты и их заказы (включая клиентов без заказов) SELECT    c.customer_id,     c.first_name,     c.last_name,     o.order_id,     o.order_date,     o.total_amount FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id;  -- Найти клиентов без заказов SELECT    c.customer_id,     c.first_name,     c.last_name,     c.registration_date FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id WHERE o.order_id IS NULL;

Множественные JOIN:

sql

-- Полная информация о заказах SELECT    o.order_id,     o.order_date,     c.first_name || ' ' || c.last_name AS customer_name,     c.email,     p.product_name,     oi.quantity,     oi.price FROM orders o INNER JOIN customers c ON o.customer_id = c.customer_id INNER JOIN order_items oi ON o.order_id = oi.order_id INNER JOIN products p ON oi.product_id = p.product_id WHERE o.status = 'completed';

4. GROUP BY — Группировка данных

GROUP BY используется для группировки строк с одинаковыми значениями в указанных столбцах. Это основа для агрегирования данных.

Агрегатные функции:

ФункцияОписаниеПример
COUNT()Количество строкCOUNT(*), COUNT(column)
SUM()Сумма значенийSUM(total_amount)
AVG()Среднее значениеAVG(price)
MAX()Максимальное значениеMAX(order_date)
MIN()Минимальное значениеMIN(price)

Примеры группировки:

sql

-- Количество заказов по статусам SELECT    status,     COUNT(*) AS order_count FROM orders GROUP BY status;  -- Сумма продаж по категориям товаров SELECT    p.category,     SUM(oi.quantity * oi.price) AS total_sales,     COUNT(DISTINCT oi.order_id) AS order_count,     SUM(oi.quantity) AS items_sold FROM order_items oi INNER JOIN products p ON oi.product_id = p.product_id GROUP BY p.category;  -- Статистика по клиентам SELECT    c.city,     COUNT(DISTINCT c.customer_id) AS customer_count,     COUNT(o.order_id) AS total_orders,     AVG(o.total_amount) AS avg_order_value,     SUM(o.total_amount) AS total_revenue FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.city;

Группировка по нескольким полям:

sql

-- Продажи по месяцам и категориям SELECT    DATE_TRUNC('month', o.order_date) AS month,     p.category,     COUNT(DISTINCT o.order_id) AS orders,     SUM(oi.quantity) AS units_sold,     SUM(oi.quantity * oi.price) AS revenue FROM orders o INNER JOIN order_items oi ON o.order_id = oi.order_id INNER JOIN products p ON oi.product_id = p.product_id WHERE o.status = 'completed' GROUP BY DATE_TRUNC('month', o.order_date), p.category ORDER BY month, category;

5. HAVING — Фильтрация групп

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

Различие между WHERE и HAVING:

sql

-- WHERE фильтрует до группировки SELECT    city,     COUNT(*) AS customer_count FROM customers WHERE registration_date >= '2023-01-01'  -- Фильтр до группировки GROUP BY city;  -- HAVING фильтрует после группировки SELECT    city,     COUNT(*) AS customer_count FROM customers GROUP BY city HAVING COUNT(*) > 1;  -- Фильтр после группировки

Практические примеры:

sql

-- Категории товаров с общей стоимостью на складе > 100000 SELECT    category,     SUM(price * stock_quantity) AS total_value,     COUNT(*) AS product_count FROM products GROUP BY category HAVING SUM(price * stock_quantity) > 100000;  -- Клиенты с более чем 2 заказами SELECT    c.customer_id,     c.first_name,     c.last_name,     COUNT(o.order_id) AS order_count,     SUM(o.total_amount) AS total_spent FROM customers c INNER JOIN orders o ON c.customer_id = o.customer_id WHERE o.status = 'completed' GROUP BY c.customer_id, c.first_name, c.last_name HAVING COUNT(o.order_id) > 2;  -- Товары со средним количеством в заказе > 2 SELECT    p.product_id,     p.product_name,     AVG(oi.quantity) AS avg_quantity,     COUNT(DISTINCT oi.order_id) AS times_ordered FROM products p INNER JOIN order_items oi ON p.product_id = oi.product_id GROUP BY p.product_id, p.product_name HAVING AVG(oi.quantity) > 2;

6. ORDER BY — Сортировка результатов

ORDER BY используется для сортировки результатов запроса по одному или нескольким столбцам.

Синтаксис и опции:

sql

-- Сортировка по возрастанию (по умолчанию) SELECT * FROM products ORDER BY price;  -- Явное указание направления сортировки SELECT * FROM products ORDER BY price ASC;  -- По возрастанию  -- Сортировка по убыванию SELECT * FROM products ORDER BY price DESC;  -- Сортировка по нескольким столбцам SELECT * FROM products ORDER BY category ASC, price DESC;

Практические примеры:

sql

-- Топ-5 самых дорогих товаров SELECT    product_name,     category,     price FROM products ORDER BY price DESC LIMIT 5;  -- Последние заказы SELECT    o.order_id,     o.order_date,     c.first_name || ' ' || c.last_name AS customer,     o.total_amount FROM orders o INNER JOIN customers c ON o.customer_id = c.customer_id ORDER BY o.order_date DESC LIMIT 10;  -- Рейтинг клиентов по сумме покупок SELECT    c.customer_id,     c.first_name,     c.last_name,     COUNT(o.order_id) AS orders_count,     SUM(o.total_amount) AS total_spent FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.customer_id, c.first_name, c.last_name ORDER BY total_spent DESC NULLS LAST;

Сортировка с вычисляемыми полями:

sql

-- Сортировка по вычисляемому полю SELECT    product_name,     price,     stock_quantity,     price * stock_quantity AS total_value FROM products ORDER BY total_value DESC;  -- Сортировка с использованием CASE SELECT    order_id,     total_amount,     status,     CASE        WHEN status = 'pending' THEN 1         WHEN status = 'processing' THEN 2         WHEN status = 'completed' THEN 3         WHEN status = 'cancelled' THEN 4     END AS status_priority FROM orders ORDER BY status_priority, order_date DESC;

7. SUBQUERY — Вложенные запросы

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

Подзапросы в WHERE:

sql

-- Заказы с суммой выше средней SELECT * FROM orders WHERE total_amount > (     SELECT AVG(total_amount)    FROM orders );  -- Клиенты, которые делали заказы SELECT * FROM customers WHERE customer_id IN (     SELECT DISTINCT customer_id    FROM orders );  -- Товары, которые ни разу не заказывали SELECT * FROM products WHERE product_id NOT IN (     SELECT DISTINCT product_id    FROM order_items );

Подзапросы в SELECT:

sql

-- Добавление вычисляемого поля с подзапросом SELECT    c.customer_id,     c.first_name,     c.last_name,     (SELECT COUNT(*)     FROM orders o     WHERE o.customer_id = c.customer_id) AS order_count,     (SELECT SUM(total_amount)     FROM orders o     WHERE o.customer_id = c.customer_id) AS total_spent FROM customers c;  -- Процент от общей суммы продаж SELECT    category,     SUM(price * stock_quantity) AS category_value,     ROUND(         SUM(price * stock_quantity) * 100.0 /        (SELECT SUM(price * stock_quantity) FROM products),         2     ) AS percentage FROM products GROUP BY category;

Подзапросы в FROM:

sql

-- Использование подзапроса как временной таблицы SELECT    month,     revenue,     revenue - LAG(revenue) OVER (ORDER BY month) AS month_over_month FROM (     SELECT        DATE_TRUNC('month', order_date) AS month,         SUM(total_amount) AS revenue    FROM orders    WHERE status = 'completed'     GROUP BY DATE_TRUNC('month', order_date) ) AS monthly_revenue;  -- Анализ распределения заказов по клиентам SELECT    order_frequency,     COUNT(*) AS customer_count FROM (     SELECT        customer_id,         COUNT(*) AS order_frequency    FROM orders    GROUP BY customer_id ) AS customer_orders GROUP BY order_frequency ORDER BY order_frequency;

8. UNION — Объединение результатов

UNION используется для объединения результатов нескольких SELECT-запросов в один результирующий набор.

Типы объединений:

ОператорОписаниеДубликаты
UNIONОбъединяет и удаляет дубликатыУдаляет
UNION ALLОбъединяет все строкиСохраняет
INTERSECTВозвращает общие строкиУдаляет
EXCEPTВозвращает строки из первого запроса, которых нет во второмУдаляет

Примеры использования UNION:

sql

-- Объединение клиентов и поставщиков в один список SELECT    'Customer' AS type,     first_name || ' ' || last_name AS name,     email,     city FROM customers UNION SELECT    'Supplier' AS type,     supplier_name AS name,     supplier_email AS email,     supplier_city AS city FROM suppliers;  -- Все транзакции (покупки и возвраты) SELECT    order_id AS transaction_id,     'Sale' AS transaction_type,     order_date AS transaction_date,     total_amount AS amount FROM orders WHERE status = 'completed' UNION ALL SELECT    return_id AS transaction_id,     'Return' AS transaction_type,     return_date AS transaction_date,     -return_amount AS amount FROM returns;

Практические сценарии:

sql

-- Создание сводного отчета по активности SELECT    'Новые клиенты' AS metric,     COUNT(*) AS value FROM customers WHERE registration_date >= CURRENT_DATE - INTERVAL '30 days' UNION ALL SELECT    'Новые заказы' AS metric,     COUNT(*) AS value FROM orders WHERE order_date >= CURRENT_DATE - INTERVAL '30 days' UNION ALL SELECT    'Отмененные заказы' AS metric,     COUNT(*) AS value FROM orders WHERE status = 'cancelled'   AND order_date >= CURRENT_DATE - INTERVAL '30 days';  -- Комбинированный поиск по разным таблицам SELECT    'Product' AS result_type,     product_id AS id,     product_name AS name,     'Category: ' || category AS description FROM products WHERE product_name LIKE '%ноут%' UNION SELECT    'Customer' AS result_type,     customer_id AS id,     first_name || ' ' || last_name AS name,     'Email: ' || email AS description FROM customers WHERE first_name LIKE '%ноут%'   OR last_name LIKE '%ноут%';

9. WINDOW FUNCTIONS — Оконные функции

Оконные функции выполняют вычисления по набору строк, связанных с текущей строкой, но в отличие от GROUP BY, они не сворачивают строки.

Основные оконные функции:

ФункцияОписаниеПрименение
ROW_NUMBER()Номер строкиНумерация, удаление дубликатов
RANK()Ранг с пропускамиРейтинги с учетом одинаковых значений
DENSE_RANK()Ранг без пропусковПлотные рейтинги
LAG() / LEAD()Предыдущее/следующее значениеСравнение с предыдущими периодами
SUM() OVERНакопительная суммаRunning total
AVG() OVERСкользящее среднееMoving average

Примеры ранжирования:

sql

-- Нумерация заказов по клиентам SELECT    customer_id,     order_id,     order_date,     total_amount,     ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date) AS order_number FROM orders;  -- Рейтинг продуктов по цене в каждой категории SELECT    product_name,     category,     price,     RANK() OVER (PARTITION BY category ORDER BY price DESC) AS price_rank,     DENSE_RANK() OVER (PARTITION BY category ORDER BY price DESC) AS dense_rank FROM products;  -- Топ-3 товара в каждой категории WITH ranked_products AS (     SELECT        product_name,         category,         price,         ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS rn    FROM products ) SELECT * FROM ranked_products WHERE rn <= 3;

Аналитические функции:

sql

-- Сравнение с предыдущим периодом SELECT    order_date,     total_amount,     LAG(total_amount, 1) OVER (ORDER BY order_date) AS previous_order,     total_amount - LAG(total_amount, 1) OVER (ORDER BY order_date) AS difference FROM orders;  -- Накопительная сумма продаж SELECT    order_date,     total_amount,     SUM(total_amount) OVER (ORDER BY order_date) AS running_total,     AVG(total_amount) OVER (ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_avg_7 FROM orders WHERE status = 'completed';  -- Процент от общей суммы SELECT    category,     product_name,     price,     SUM(price) OVER (PARTITION BY category) AS category_total,     ROUND(price * 100.0 / SUM(price) OVER (PARTITION BY category), 2) AS percentage FROM products;

10. CTE (Common Table Expressions) — Временные именованные результаты

CTE позволяют создавать временные именованные результаты запросов, которые существуют только в рамках одного SQL-выражения.

Синтаксис и преимущества:

sql

-- Базовый синтаксис CTE WITH cte_name AS (     SELECT ... ) SELECT * FROM cte_name;  -- Множественные CTE WITH first_cte AS (     SELECT ... ), second_cte AS (     SELECT ... ) SELECT * FROM first_cte JOIN second_cte ON ...;

Практические примеры:

sql

-- Анализ клиентской базы с использованием CTE WITH customer_stats AS (     SELECT        c.customer_id,         c.first_name,         c.last_name,         COUNT(o.order_id) AS order_count,         SUM(o.total_amount) AS total_spent,         AVG(o.total_amount) AS avg_order_value    FROM customers c    LEFT JOIN orders o ON c.customer_id = o.customer_id    GROUP BY c.customer_id, c.first_name, c.last_name ), customer_segments AS (     SELECT        *,         CASE            WHEN total_spent > 10000 THEN 'VIP'             WHEN total_spent > 5000 THEN 'Gold'             WHEN total_spent > 0 THEN 'Regular'             ELSE 'Inactive'         END AS segment    FROM customer_stats ) SELECT    segment,     COUNT(*) AS customer_count,     AVG(order_count) AS avg_orders,     AVG(total_spent) AS avg_revenue FROM customer_segments GROUP BY segment ORDER BY avg_revenue DESC;

Рекурсивные CTE:

sql

-- Иерархическая структура категорий WITH RECURSIVE category_tree AS (     -- Базовый случай: корневые категории     SELECT        category_id,         category_name,         parent_id,         0 AS level,         category_name AS path    FROM categories    WHERE parent_id IS NULL        UNION ALL        -- Рекурсивная часть     SELECT        c.category_id,         c.category_name,         c.parent_id,         ct.level + 1,         ct.path || ' > ' || c.category_name    FROM categories c    INNER JOIN category_tree ct ON c.parent_id = ct.category_id ) SELECT * FROM category_tree ORDER BY path;

Сложные аналитические запросы с CTE:

sql

-- Когортный анализ удержания клиентов WITH cohorts AS (     SELECT        customer_id,         DATE_TRUNC('month', registration_date) AS cohort_month    FROM customers ), customer_orders AS (     SELECT        o.customer_id,         DATE_TRUNC('month', o.order_date) AS order_month,         c.cohort_month    FROM orders o    INNER JOIN cohorts c ON o.customer_id = c.customer_id ), cohort_data AS (     SELECT        cohort_month,         order_month,         COUNT(DISTINCT customer_id) AS customers    FROM customer_orders    GROUP BY cohort_month, order_month ), cohort_size AS (     SELECT        cohort_month,         COUNT(DISTINCT customer_id) AS total_customers    FROM cohorts    GROUP BY cohort_month ) SELECT    cd.cohort_month,     cd.order_month,     cd.customers,     cs.total_customers,     ROUND(cd.customers * 100.0 / cs.total_customers, 2) AS retention_rate FROM cohort_data cd INNER JOIN cohort_size cs ON cd.cohort_month = cs.cohort_month ORDER BY cd.cohort_month, cd.order_month;

Практические кейсы для аналитиков

Кейс 1: Анализ продаж по периодам

sql

-- Сравнение продаж по месяцам WITH monthly_sales AS (     SELECT        DATE_TRUNC('month', order_date) AS month,         COUNT(DISTINCT order_id) AS orders,         COUNT(DISTINCT customer_id) AS unique_customers,         SUM(total_amount) AS revenue,         AVG(total_amount) AS avg_order_value    FROM orders    WHERE status = 'completed'     GROUP BY DATE_TRUNC('month', order_date) ) SELECT    month,     orders,     unique_customers,     revenue,     avg_order_value,     LAG(revenue) OVER (ORDER BY month) AS prev_month_revenue,     ROUND((revenue - LAG(revenue) OVER (ORDER BY month)) * 100.0 /          NULLIF(LAG(revenue) OVER (ORDER BY month), 0), 2) AS growth_rate FROM monthly_sales ORDER BY month;

Кейс 2: ABC-анализ товаров

sql

-- ABC-анализ товарного ассортимента WITH product_sales AS (     SELECT        p.product_id,         p.product_name,         p.category,         SUM(oi.quantity) AS units_sold,         SUM(oi.quantity * oi.price) AS revenue    FROM products p    LEFT JOIN order_items oi ON p.product_id = oi.product_id    GROUP BY p.product_id, p.product_name, p.category ), product_ranking AS (     SELECT        *,         SUM(revenue) OVER (ORDER BY revenue DESC) AS cumulative_revenue,         SUM(revenue) OVER () AS total_revenue    FROM product_sales ) SELECT    product_id,     product_name,     category,     units_sold,     revenue,     ROUND(revenue * 100.0 / NULLIF(total_revenue, 0), 2) AS revenue_percentage,     ROUND(cumulative_revenue * 100.0 / NULLIF(total_revenue, 0), 2) AS cumulative_percentage,     CASE        WHEN cumulative_revenue * 100.0 / NULLIF(total_revenue, 0) <= 80 THEN 'A'         WHEN cumulative_revenue * 100.0 / NULLIF(total_revenue, 0) <= 95 THEN 'B'         ELSE 'C'     END AS abc_category FROM product_ranking ORDER BY revenue DESC;

Кейс 3: Анализ воронки продаж

sql

-- Построение воронки конверсии WITH funnel_stages AS (     SELECT 'Посетители сайта' AS stage, 1 AS stage_order, 10000 AS users    UNION ALL     SELECT 'Зарегистрировались', 2, (SELECT COUNT(*) FROM customers)     UNION ALL     SELECT 'Сделали первый заказ', 3, (SELECT COUNT(DISTINCT customer_id) FROM orders)     UNION ALL     SELECT 'Повторная покупка', 4,           (SELECT COUNT(DISTINCT customer_id)            FROM (SELECT customer_id, COUNT(*) AS orders                  FROM orders                  GROUP BY customer_id                  HAVING COUNT(*) > 1) t) ) SELECT    stage,     users,     LAG(users) OVER (ORDER BY stage_order) AS prev_stage_users,     ROUND(users * 100.0 / FIRST_VALUE(users) OVER (ORDER BY stage_order), 2) AS total_conversion,     ROUND(users * 100.0 / NULLIF(LAG(users) OVER (ORDER BY stage_order), 0), 2) AS stage_conversion FROM funnel_stages ORDER BY stage_order;

Оптимизация запросов

Основные принципы оптимизации

ПроблемаРешениеПример
Полное сканирование таблицыИспользование индексовCREATE INDEX idx_customer_email ON customers(email);
SELECT *Выбор только нужных столбцовSELECT id, name вместо SELECT *
Множественные подзапросыИспользование JOIN или CTEWITH ... SELECT вместо вложенных SELECT
Отсутствие фильтрацииДобавление WHEREWHERE date >= '2023-01-01'
Неоптимальный JOINПравильный порядок таблицСначала маленькие таблицы

Использование EXPLAIN

sql

-- Анализ плана выполнения запроса EXPLAIN (ANALYZE, BUFFERS) SELECT    c.customer_id,     COUNT(o.order_id) AS orders FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.customer_id;

Индексы и их влияние

sql

-- Создание индексов для часто используемых полей CREATE INDEX idx_orders_customer_id ON orders(customer_id); CREATE INDEX idx_orders_date_status ON orders(order_date, status); CREATE INDEX idx_products_category_price ON products(category, price);  -- Составной индекс для сложных условий CREATE INDEX idx_orders_composite ON orders(customer_id, order_date, status) WHERE status = 'completed';

Полезные функции и операторы

Работа с датами

sql

-- Извлечение частей даты SELECT    order_date,     EXTRACT(YEAR FROM order_date) AS year,     EXTRACT(MONTH FROM order_date) AS month,     EXTRACT(DAY FROM order_date) AS day,     EXTRACT(WEEK FROM order_date) AS week,     EXTRACT(QUARTER FROM order_date) AS quarter FROM orders;  -- Арифметика дат SELECT    order_date,     order_date + INTERVAL '7 days' AS week_later,     order_date - INTERVAL '1 month' AS month_ago,     CURRENT_DATE - order_date AS days_ago FROM orders;  -- Форматирование дат SELECT    order_date,     TO_CHAR(order_date, 'DD.MM.YYYY') AS formatted_date,     TO_CHAR(order_date, 'Day, DD Month YYYY') AS long_date,     TO_CHAR(order_date, 'Q') AS quarter FROM orders;

Работа со строками

sql

-- Функции обработки строк SELECT    first_name,     UPPER(first_name) AS uppercase,     LOWER(first_name) AS lowercase,     LENGTH(first_name) AS length,     SUBSTRING(email FROM 1 FOR POSITION('@' IN email) - 1) AS email_name,     REPLACE(email, '@', ' at ') AS replaced_email,     TRIM('  ' FROM first_name) AS trimmed FROM customers;  -- Регулярные выражения SELECT * FROM customers WHERE email ~ '^[a-zA-Z0-9._%+-]+@gmail\.com$';

Условная логика

sql

-- CASE выражения SELECT    order_id,     total_amount,     CASE        WHEN total_amount < 1000 THEN 'Small'         WHEN total_amount < 5000 THEN 'Medium'         WHEN total_amount < 10000 THEN 'Large'         ELSE 'VIP'     END AS order_size,     CASE status         WHEN 'completed' THEN 'Завершен'         WHEN 'cancelled' THEN 'Отменен'         WHEN 'pending' THEN 'Ожидает'         ELSE 'Неизвестно'     END AS status_ru FROM orders;  -- COALESCE и NULLIF SELECT    product_name,     COALESCE(discount_price, price) AS final_price,     COALESCE(description, 'Описание отсутствует') AS description,     NULLIF(stock_quantity, 0) AS available_stock FROM products;

Работа с различными СУБД

Особенности синтаксиса разных СУБД

ФункцияPostgreSQLMySQLSQL ServerOracle
Ограничение строкLIMIT 10LIMIT 10TOP 10ROWNUM <= 10
Текущая датаCURRENT_DATECURDATE()GETDATE()SYSDATE
Конкатенация||CONCAT()+||
Извлечение годаEXTRACT(YEAR FROM date)YEAR(date)YEAR(date)EXTRACT(YEAR FROM date)
Преобразование типовCAST(x AS type)CAST(x AS type)CAST(x AS type)TO_NUMBER, TO_DATE

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

sql

-- PostgreSQL SELECT first_name || ' ' || last_name AS full_name FROM customers LIMIT 10;  -- MySQL SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM customers LIMIT 10;  -- SQL Server SELECT TOP 10 first_name + ' ' + last_name AS full_name FROM customers;  -- Oracle SELECT first_name || ' ' || last_name AS full_name FROM customers WHERE ROWNUM <= 10;

Практические задания для закрепления

Задание 1: Базовые запросы

  1. Выберите всех клиентов из Москвы, зарегистрированных в 2023 году
  2. Найдите товары с ценой от 1000 до 5000 рублей
  3. Отсортируйте заказы по дате в обратном порядке

Задание 2: Группировка и агрегация

  1. Посчитайте количество заказов и общую сумму продаж по месяцам
  2. Найдите среднюю стоимость товара в каждой категории
  3. Определите топ-5 клиентов по сумме покупок

Задание 3: Объединения таблиц

  1. Получите список заказов с информацией о клиентах
  2. Найдите товары, которые ни разу не заказывали
  3. Создайте полный отчет по заказам с деталями

Задание 4: Оконные функции

  1. Пронумеруйте заказы каждого клиента
  2. Рассчитайте накопительную сумму продаж по дням
  3. Найдите разницу между текущим и предыдущим заказом

Задание 5: Комплексный анализ

  1. Проведите RFM-анализ клиентской базы
  2. Постройте когортный анализ по месяцам регистрации
  3. Создайте дашборд основных метрик продаж

Типичные ошибки начинающих

Ошибки в синтаксисе и логике

ОшибкаНеправильноПравильно
Группировка без агрегацииSELECT name, city FROM customers GROUP BY citySELECT city, COUNT(*) FROM customers GROUP BY city
NULL в сравненияхWHERE phone = NULLWHERE phone IS NULL
Деление на нольrevenue / usersrevenue / NULLIF(users, 0)
Неправильный JOINFROM orders, customersFROM orders JOIN customers ON ...
HAVING вместо WHERESELECT * FROM orders HAVING status = 'completed'SELECT * FROM orders WHERE status = 'completed'

Проблемы производительности

sql

-- Плохо: подзапрос выполняется для каждой строки SELECT    *,     (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.customer_id) AS orders FROM customers c;  -- Хорошо: JOIN с группировкой SELECT    c.*,     COALESCE(o.order_count, 0) AS orders FROM customers c LEFT JOIN (     SELECT customer_id, COUNT(*) AS order_count    FROM orders    GROUP BY customer_id ) o ON c.customer_id = o.customer_id;

Советы по изучению SQL

План обучения для начинающих

  1. Неделя 1-2: Основы SELECT, WHERE, ORDER BY
  2. Неделя 3-4: JOIN, GROUP BY, агрегатные функции
  3. Неделя 5-6: Подзапросы, UNION, CASE
  4. Неделя 7-8: Оконные функции, CTE
  5. Далее: Оптимизация, специфика СУБД, практические проекты

Рекомендации по практике

  1. Используйте реальные данные — работайте с открытыми датасетами
  2. Решайте практические задачи — участвуйте в SQL-челленджах
  3. Анализируйте чужой код — изучайте решения опытных аналитиков
  4. Документируйте запросы — пишите понятные комментарии
  5. Тестируйте на разных данных — проверяйте граничные случаи

Ресурсы для самостоятельного изучения

Онлайн-платформы:

  • SQLZoo — интерактивные уроки
  • HackerRank SQL — задачи разной сложности
  • LeetCode Database — подготовка к собеседованиям
  • Mode Analytics SQL Tutorial — практические кейсы
  • W3Schools SQL — справочник с примерами

Практические датасеты:

  • Kaggle Datasets — тысячи реальных датасетов
  • Google BigQuery Public Datasets — большие данные
  • PostgreSQL Sample Databases — учебные базы
  • MySQL Sample Databases — примеры схем

Заключение

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

Ключевые принципы успешного освоения SQL:

  • Регулярная практика — пишите запросы каждый день
  • Понимание данных — изучайте структуру и связи таблиц
  • Оптимизация — думайте о производительности
  • Читаемость — пишите понятный и документированный код
  • Непрерывное обучение — изучайте новые функции и подходы

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

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