Задачи среднего уровня по SQL: JOIN, подзапросы и их решения
Пусть ИИ работает за вас
Как собрать ИИ-ассистента для себя с нуля
Мини-курс для новичков
Перейти

Задачи среднего уровня по SQL: JOIN, подзапросы и их решения

#Разное  
Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Специалисты, стремящиеся улучшить свои навыки в SQL и повысить свою конкурентоспособность на рынке труда
  • Аналитики данных и разработчики, работающие с реляционными базами данных
  • Люди, готовящиеся к техническим собеседованиям в области данных и аналитики

SQL — не просто язык запросов, а мощный инструмент аналитики данных, владение которым на среднем уровне открывает двери в мир высокооплачиваемых специальностей. Освоив JOIN-операции и подзапросы, вы сможете решать 80% реальных задач в работе с данными, а это именно то, что проверяют на технических собеседованиях. По статистике HeadHunter, специалисты, владеющие продвинутыми техниками SQL, зарабатывают на 30-40% больше тех, кто ограничивается базовыми знаниями. Предлагаю погрузиться в практические задачи, которые не только подготовят вас к собеседованиям, но и сделают работу с базами данных по-настоящему эффективной. 🚀

Ключевые типы JOIN-операций в практических задачах SQL

JOIN-операции — краеугольный камень работы с реляционными базами данных. Понимание различных типов JOIN позволяет эффективно связывать данные из нескольких таблиц, получая необходимую информацию без избыточных запросов. 👨‍💻

Рассмотрим классические задачи с использованием основных типов JOIN на примере базы данных интернет-магазина с таблицами Customers, Orders и Products.

Задача 1: INNER JOIN

Найти всех клиентов, сделавших заказы в последний месяц, и общую сумму их заказов.

SELECT c.customer_id, c.name, SUM(o.total_amount) AS total_spent
FROM Customers c
INNER JOIN Orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= DATEADD(month, -1, GETDATE())
GROUP BY c.customer_id, c.name
ORDER BY total_spent DESC;

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

Задача 2: LEFT JOIN

Вывести список всех клиентов и количество их заказов, включая тех, кто ещё ничего не заказывал.

SELECT c.customer_id, c.name, COUNT(o.order_id) AS order_count
FROM Customers c
LEFT JOIN Orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name
ORDER BY order_count DESC;

LEFT JOIN сохраняет все строки из левой таблицы (Customers) и добавляет соответствующие строки из правой таблицы (Orders). Если соответствия нет, поля из правой таблицы будут содержать NULL.

Задача 3: FULL OUTER JOIN

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

SELECT p.product_id, p.product_name, o.order_id
FROM Products p
FULL OUTER JOIN Orders o ON p.product_id = o.product_id
WHERE p.product_id IS NULL OR o.order_id IS NULL;

FULL OUTER JOIN возвращает все строки из обеих таблиц, соединяя их там, где есть совпадение, и заполняя NULL, где совпадений нет.

Александр Петров, Lead Data Engineer На моём последнем проекте мы столкнулись с необходимостью анализа цепочек поставок для крупной розничной сети. Критически важно было выявить товары, которые "застревали" на определённых этапах логистического процесса. Изначально команда использовала несколько последовательных запросов, что приводило к задержкам при формировании отчётов.

Мы перепроектировали решение, используя серию LEFT JOIN между таблицами поставок, складских запасов и отгрузок. Ключевым моментом стало добавление временных меток для отслеживания движения каждой партии товаров. В результате удалось не только выявить проблемные участки, но и сократить время формирования аналитики с 40 минут до 3 минут. Правильное использование JOIN-операций с корректными условиями соединения повысило производительность системы в 13 раз без изменения архитектуры базы данных.

Сравним типы JOIN-операций по их характеристикам и применимости:

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

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

Пошаговый план для смены профессии

Эффективное использование подзапросов в SQL-задачах

Подзапросы (subqueries) — мощный инструмент SQL, позволяющий выполнять сложные многоуровневые операции в рамках одного запроса. По сути, это запросы, вложенные в другие запросы, которые могут возвращать одно значение, список значений или целую таблицу результатов. 🧩

Выделяют три основных типа подзапросов:

  • Скалярные подзапросы — возвращают одно значение
  • Строчные подзапросы — возвращают одну строку с несколькими значениями
  • Табличные подзапросы — возвращают таблицу результатов

Задача 1: Скалярный подзапрос

Найти товары, цена которых выше средней цены в категории.

SELECT product_id, product_name, price, category_id
FROM Products
WHERE price > (
SELECT AVG(price) 
FROM Products p2 
WHERE p2.category_id = Products.category_id
);

Обратите внимание на коррелированный подзапрос, который ссылается на основной запрос (Products.category_id). Это позволяет вычислять среднюю цену отдельно для каждой категории.

Задача 2: Подзапрос в FROM

Определить топ-3 самых активных клиентов по каждому региону.

SELECT region, customer_id, name, order_count
FROM (
SELECT c.region, c.customer_id, c.name,
COUNT(o.order_id) AS order_count,
ROW_NUMBER() OVER (PARTITION BY c.region ORDER BY COUNT(o.order_id) DESC) AS rank
FROM Customers c
JOIN Orders o ON c.customer_id = o.customer_id
GROUP BY c.region, c.customer_id, c.name
) ranked
WHERE rank <= 3
ORDER BY region, rank;

Здесь мы используем подзапрос в секции FROM для создания временного набора данных с ранжированием клиентов внутри каждого региона.

Задача 3: Подзапрос с EXISTS

Найти клиентов, которые заказывали товары из категории "Электроника", но никогда не заказывали товары из категории "Аксессуары".

SELECT DISTINCT c.customer_id, c.name
FROM Customers c
JOIN Orders o ON c.customer_id = o.customer_id
JOIN OrderItems oi ON o.order_id = oi.order_id
JOIN Products p ON oi.product_id = p.product_id
WHERE p.category_id = (SELECT category_id FROM Categories WHERE name = 'Электроника')
AND NOT EXISTS (
SELECT 1
FROM Orders o2
JOIN OrderItems oi2 ON o2.order_id = oi2.order_id
JOIN Products p2 ON oi2.product_id = p2.product_id
WHERE o2.customer_id = c.customer_id
AND p2.category_id = (SELECT category_id FROM Categories WHERE name = 'Аксессуары')
);

Оператор EXISTS проверяет наличие хотя бы одной строки в подзапросе. NOT EXISTS, соответственно, проверяет отсутствие строк, что делает его идеальным для реализации логики "клиенты, которые никогда не...".

Задача 4: Подзапрос в SELECT

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

SELECT 
p.product_id,
p.product_name,
p.price,
(
SELECT AVG(price) 
FROM Products 
WHERE category_id = p.category_id
) AS avg_category_price,
(p.price – (SELECT AVG(price) FROM Products WHERE category_id = p.category_id)) / 
(SELECT AVG(price) FROM Products WHERE category_id = p.category_id) * 100 AS price_deviation_percent
FROM 
Products p
ORDER BY 
p.category_id, price_deviation_percent DESC;

Здесь подзапросы используются непосредственно в списке выборки (SELECT) для расчета дополнительных значений для каждой строки основного результата.

Преимущества использования подзапросов:

  • Модульность и читаемость кода
  • Возможность выполнения сложных многоуровневых операций в одном запросе
  • Динамическое вычисление значений для фильтрации или агрегации

Недостатки и ограничения:

  • Потенциальное снижение производительности, особенно при коррелированных подзапросах
  • Ограниченная поддержка в некоторых СУБД
  • Сложность отладки при многоуровневой вложенности

Комбинирование JOIN и подзапросов для сложных выборок

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

Рассмотрим несколько примеров, демонстрирующих мощь такого подхода:

Задача 1: Анализ продаж в разрезе категорий с историческим сравнением

Сравнить текущие продажи по категориям с аналогичным периодом прошлого года и рассчитать процент роста.

SELECT 
c.category_name,
current_period.sales AS current_sales,
previous_period.sales AS previous_sales,
(current_period.sales – previous_period.sales) / previous_period.sales * 100 AS growth_percent
FROM 
Categories c
JOIN (
-- Продажи за текущий период
SELECT 
p.category_id,
SUM(oi.quantity * oi.price) AS sales
FROM 
Products p
JOIN 
OrderItems oi ON p.product_id = oi.product_id
JOIN 
Orders o ON oi.order_id = o.order_id
WHERE 
o.order_date BETWEEN DATEADD(month, -1, GETDATE()) AND GETDATE()
GROUP BY 
p.category_id
) current_period ON c.category_id = current_period.category_id
JOIN (
-- Продажи за аналогичный период прошлого года
SELECT 
p.category_id,
SUM(oi.quantity * oi.price) AS sales
FROM 
Products p
JOIN 
OrderItems oi ON p.product_id = oi.product_id
JOIN 
Orders o ON oi.order_id = o.order_id
WHERE 
o.order_date BETWEEN DATEADD(month, -13, GETDATE()) AND DATEADD(month, -12, GETDATE())
GROUP BY 
p.category_id
) previous_period ON c.category_id = previous_period.category_id
WHERE 
previous_period.sales > 0 -- Избегаем деления на ноль
ORDER BY 
growth_percent DESC;

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

Задача 2: Выявление аномалий в поведении клиентов

Найти клиентов, чей средний чек за последний месяц значительно (более чем на 50%) превышает их историческое среднее.

SELECT 
c.customer_id,
c.name,
recent_avg.avg_order AS recent_average_order,
historical_avg.avg_order AS historical_average_order,
(recent_avg.avg_order – historical_avg.avg_order) / historical_avg.avg_order * 100 AS increase_percent
FROM 
Customers c
JOIN (
-- Средний чек за последний месяц
SELECT 
customer_id,
AVG(total_amount) AS avg_order
FROM 
Orders
WHERE 
order_date >= DATEADD(month, -1, GETDATE())
GROUP BY 
customer_id
) recent_avg ON c.customer_id = recent_avg.customer_id
JOIN (
-- Исторический средний чек (до последнего месяца)
SELECT 
customer_id,
AVG(total_amount) AS avg_order
FROM 
Orders
WHERE 
order_date < DATEADD(month, -1, GETDATE())
GROUP BY 
customer_id
) historical_avg ON c.customer_id = historical_avg.customer_id
WHERE 
(recent_avg.avg_order – historical_avg.avg_order) / historical_avg.avg_order > 0.5
ORDER BY 
increase_percent DESC;

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

Мария Сорокина, Data Analyst Работая в команде аналитиков крупного маркетплейса, я столкнулась со сложной задачей выявления подозрительных транзакций, потенциально связанных с мошенничеством. Традиционный подход с использованием нескольких последовательных запросов создавал высокую нагрузку на сервер и требовал сложной синхронизации временных таблиц.

Ключом к решению стала комбинация нескольких JOIN-операций с вложенными подзапросами. Мы создали запрос, который анализировал паттерны покупок, сопоставляя текущие транзакции с историческими данными пользователя и средними показателями по сегменту. Особенно эффективным оказалось использование оконных функций (OVER, PARTITION BY) внутри подзапросов для расчета скользящих средних и стандартных отклонений.

Результат превзошел ожидания: система стала выявлять подозрительные транзакции в режиме реального времени с точностью 89%, что на 27% выше предыдущего алгоритма. А время обработки данных сократилось с нескольких часов до 15 минут. Теперь этот подход стал стандартом для обнаружения аномалий не только в транзакциях, но и в других бизнес-процессах компании.

Задача 3: Многоуровневая категоризация клиентов

Разделить клиентов на категории по активности (по количеству заказов) и по ценности (по среднему чеку), а затем создать комбинированную матрицу сегментации.

WITH CustomerActivity AS (
SELECT 
customer_id,
COUNT(order_id) AS order_count,
CASE 
WHEN COUNT(order_id) >= 10 THEN 'High'
WHEN COUNT(order_id) >= 5 THEN 'Medium'
ELSE 'Low'
END AS activity_segment
FROM 
Orders
WHERE 
order_date >= DATEADD(year, -1, GETDATE())
GROUP BY 
customer_id
),
CustomerValue AS (
SELECT 
customer_id,
AVG(total_amount) AS avg_order_value,
CASE 
WHEN AVG(total_amount) >= 500 THEN 'Premium'
WHEN AVG(total_amount) >= 100 THEN 'Standard'
ELSE 'Economy'
END AS value_segment
FROM 
Orders
WHERE 
order_date >= DATEADD(year, -1, GETDATE())
GROUP BY 
customer_id
)
SELECT 
c.customer_id,
c.name,
c.email,
ca.order_count,
ca.activity_segment,
cv.avg_order_value,
cv.value_segment,
ca.activity_segment + '-' + cv.value_segment AS combined_segment
FROM 
Customers c
JOIN 
CustomerActivity ca ON c.customer_id = ca.customer_id
JOIN 
CustomerValue cv ON c.customer_id = cv.customer_id
ORDER BY 
CASE 
WHEN ca.activity_segment = 'High' AND cv.value_segment = 'Premium' THEN 1
WHEN ca.activity_segment = 'High' AND cv.value_segment = 'Standard' THEN 2
WHEN ca.activity_segment = 'Medium' AND cv.value_segment = 'Premium' THEN 3
-- ... и так далее для других комбинаций
ELSE 9
END;

Здесь мы используем общие табличные выражения (CTE) вместе с JOIN для создания многомерной сегментации клиентов.

Советы по эффективному комбинированию JOIN и подзапросов:

  • Начинайте с разработки отдельных частей запроса и постепенно объединяйте их
  • Используйте CTE (Common Table Expressions) для повышения читаемости сложных запросов
  • Проверяйте план выполнения запроса для выявления потенциальных проблем производительности
  • При многократном использовании одного и того же подзапроса, выносите его в CTE
  • Учитывайте порядок выполнения JOIN и WHERE условий при оптимизации запросов

Оптимизация SQL-запросов для повышения производительности

Написать SQL-запрос, выполняющий нужную функцию — полдела. Создать запрос, работающий быстро даже на больших объемах данных — настоящее искусство. Оптимизация запросов с JOIN и подзапросами становится особенно важной по мере роста объема данных и сложности аналитических задач. 🚀

Основные факторы, влияющие на производительность запросов:

  • Стратегия соединения таблиц (join strategy)
  • Наличие и эффективность индексов
  • Порядок соединения таблиц
  • Способ использования подзапросов
  • Объем данных, обрабатываемых на каждом этапе

Задача 1: Оптимизация запроса с множественными JOIN

Рассмотрим запрос, который выглядит безобидно, но может работать неэффективно:

-- Неоптимизированная версия
SELECT 
c.name AS customer_name,
p.product_name,
o.order_date,
oi.quantity,
oi.price
FROM 
Customers c
JOIN 
Orders o ON c.customer_id = o.customer_id
JOIN 
OrderItems oi ON o.order_id = oi.order_id
JOIN 
Products p ON oi.product_id = p.product_id
WHERE 
o.order_date >= '2023-01-01'
ORDER BY 
o.order_date DESC;

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

-- Оптимизированная версия
SELECT 
c.name AS customer_name,
p.product_name,
o.order_date,
oi.quantity,
oi.price
FROM 
Orders o -- Начинаем с таблицы, к которой применяется фильтр по дате
JOIN 
OrderItems oi ON o.order_id = oi.order_id
JOIN 
Products p ON oi.product_id = p.product_id
JOIN 
Customers c ON o.customer_id = c.customer_id
WHERE 
o.order_date >= '2023-01-01'
ORDER BY 
o.order_date DESC;

Ключевые оптимизации:

  • Изменение порядка JOIN: начинаем с таблицы Orders, к которой применяется фильтр по дате, чтобы сразу уменьшить набор обрабатываемых данных
  • Порядок соединения таблиц выбран с учетом кардинальности отношений (от меньшего результирующего набора к большему)

Задача 2: Замена подзапроса на JOIN

Часто подзапросы можно переписать с использованием JOIN, что может повысить производительность:

-- Версия с подзапросом
SELECT 
p.product_id,
p.product_name,
p.price
FROM 
Products p
WHERE 
p.category_id IN (
SELECT category_id 
FROM Categories 
WHERE active = 1
);

-- Оптимизированная версия с JOIN
SELECT 
p.product_id,
p.product_name,
p.price
FROM 
Products p
JOIN 
Categories c ON p.category_id = c.category_id
WHERE 
c.active = 1;

Версия с JOIN часто работает быстрее, особенно если оптимизатор не может эффективно преобразовать подзапрос внутренне.

Задача 3: Оптимизация с использованием индексов

Правильно спроектированные индексы могут радикально улучшить производительность запросов с JOIN:

-- Создаем составной индекс для часто используемых условий соединения и фильтрации
CREATE INDEX idx_orders_customer_date ON Orders(customer_id, order_date);

-- Теперь запрос будет использовать этот индекс
SELECT 
c.name,
COUNT(o.order_id) AS order_count,
SUM(o.total_amount) AS total_spent
FROM 
Customers c
JOIN 
Orders o ON c.customer_id = o.customer_id
WHERE 
o.order_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY 
c.customer_id, c.name;

Задача 4: Использование оконных функций вместо подзапросов

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

-- Версия с подзапросом
SELECT 
o.order_id,
o.customer_id,
o.order_date,
o.total_amount,
(
SELECT AVG(total_amount)
FROM Orders o2
WHERE o2.customer_id = o.customer_id
) AS customer_avg_order
FROM 
Orders o
WHERE 
o.order_date >= '2023-01-01';

-- Оптимизированная версия с оконной функцией
SELECT 
o.order_id,
o.customer_id,
o.order_date,
o.total_amount,
AVG(o.total_amount) OVER (PARTITION BY o.customer_id) AS customer_avg_order
FROM 
Orders o
WHERE 
o.order_date >= '2023-01-01';

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

Сравнение различных подходов к оптимизации:

Стратегия оптимизации Потенциальный выигрыш в производительности Сложность реализации Применимость
Изменение порядка JOIN 10%-50% Низкая Почти всегда
Замена подзапроса на JOIN 20%-200% Средняя Для многих типов подзапросов
Оптимизация индексов 100%-10000% Средняя Для часто выполняемых запросов
Использование оконных функций 50%-500% Высокая ДляAggregating подзапросов
Денормализация данных 100%-1000% Очень высокая Для критически важных запросов

Советы по оптимизации:

  • Всегда анализируйте план выполнения запроса (EXPLAIN PLAN, EXPLAIN ANALYZE)
  • Тестируйте производительность запросов на реалистичных объемах данных
  • Оптимизируйте сначала самые медленные и часто выполняемые запросы
  • Учитывайте, что оптимальный план запроса может меняться при изменении объема данных
  • Не жертвуйте читаемостью ради микрооптимизаций — код должен оставаться поддерживаемым

Распространенные задачи на собеседованиях с JOIN и подзапросами

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

Давайте рассмотрим типичные задачи, которые встречаются на собеседованиях, и стратегии их решения.

Задача 1: Поиск дубликатов в данных

Найти всех клиентов, имеющих одинаковый email, но разные имена.

SELECT 
c1.customer_id AS customer_id1,
c1.name AS name1,
c2.customer_id AS customer_id2,
c2.name AS name2,
c1.email
FROM 
Customers c1
JOIN 
Customers c2 ON c1.email = c2.email
WHERE 
c1.customer_id < c2.customer_id -- Предотвращает дублирование результатов
AND c1.name != c2.name;

Ключевые моменты: использование self-join для поиска совпадений в одной таблице и условие c1.customerid < c2.customerid для исключения симметричных дубликатов в результате.

Задача 2: Анализ последовательных событий

Найти клиентов, которые сделали как минимум два заказа с интервалом не более 7 дней.

SELECT DISTINCT
o1.customer_id,
c.name
FROM 
Orders o1
JOIN 
Orders o2 ON o1.customer_id = o2.customer_id
JOIN 
Customers c ON o1.customer_id = c.customer_id
WHERE 
o1.order_id < o2.order_id -- Разные заказы
AND DATEDIFF(day, o1.order_date, o2.order_date) BETWEEN 1 AND 7 -- Интервал не более 7 дней
ORDER BY 
o1.customer_id;

Здесь мы снова используем self-join, но уже для анализа временных последовательностей событий.

Задача 3: Ранжирование с использованием оконных функций

Для каждой категории найти три самых продаваемых товара за последний год.

WITH ProductSales AS (
SELECT 
p.product_id,
p.product_name,
p.category_id,
SUM(oi.quantity) AS total_sold,
ROW_NUMBER() OVER(PARTITION BY p.category_id ORDER BY SUM(oi.quantity) DESC) AS rank
FROM 
Products p
JOIN 
OrderItems oi ON p.product_id = oi.product_id
JOIN 
Orders o ON oi.order_id = o.order_id
WHERE 
o.order_date >= DATEADD(year, -1, GETDATE())
GROUP BY 
p.product_id, p.product_name, p.category_id
)
SELECT 
c.category_name,
ps.product_name,
ps.total_sold,
ps.rank
FROM 
ProductSales ps
JOIN 
Categories c ON ps.category_id = c.category_id
WHERE 
ps.rank <= 3
ORDER BY 
c.category_name, ps.rank;

Использование оконной функции ROW_NUMBER() позволяет ранжировать товары внутри каждой категории без необходимости писать сложные подзапросы.

Задача 4: Нахождение медианного значения

Рассчитать медианную стоимость заказа для каждой категории товаров.

WITH OrderValues AS (
SELECT 
p.category_id,
oi.price * oi.quantity AS order_value,
ROW_NUMBER() OVER(PARTITION BY p.category_id ORDER BY oi.price * oi.quantity) AS row_asc,
COUNT(*) OVER(PARTITION BY p.category_id) AS category_count
FROM 
OrderItems oi
JOIN 
Products p ON oi.product_id = p.product_id
)
SELECT 
c.category_id,
c.category_name,
AVG(ov.order_value) AS median_order_value -- Среднее для двух центральных значений в случае четного количества
FROM 
OrderValues ov
JOIN 
Categories c ON ov.category_id = c.category_id
WHERE 
ov.row_asc IN ((ov.category_count + 1) / 2, (ov.category_count + 2) / 2)
GROUP BY 
c.category_id, c.category_name;

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

Задача 5: Заполнение пропусков в последовательностях

Найти пропущенные даты в последовательности ежедневных заказов.

WITH DateSequence AS (
SELECT 
DATEADD(DAY, number, MIN(order_date)) AS date
FROM 
Orders
CROSS JOIN 
master.dbo.spt_values
WHERE 
type = 'P' 
AND number BETWEEN 0 AND DATEDIFF(DAY, MIN(order_date), MAX(order_date))
GROUP BY 
number
)
SELECT 
ds.date AS missing_date
FROM 
DateSequence ds
LEFT JOIN 
Orders o ON ds.date = o.order_date
WHERE 
o.order_id IS NULL
ORDER BY 
ds.date;

Этот запрос демонстрирует использование CROSS JOIN для генерации последовательности дат, а затем LEFT JOIN для выявления дат без заказов.

Советы по подготовке к SQL-собеседованиям:

  • Практикуйтесь на реальных базах данных или датасетах (например, Northwind, AdventureWorks)
  • Учитесь не только находить решение, но и объяснять его логику и эффективность
  • Изучайте различные подходы к одной и той же задаче, сравнивайте их преимущества и недостатки
  • Обращайте внимание на особенности синтаксиса различных СУБД (MySQL, PostgreSQL, SQL Server, Oracle)
  • Тренируйтесь писать запросы без использования IDE, так как на некоторых собеседованиях могут попросить написать код на бумаге или в простом текстовом редакторе

Типичные ловушки на собеседованиях:

  • Неправильное использование агрегатных функций без GROUP BY
  • Неверное понимание разницы между LEFT JOIN и INNER JOIN
  • Ошибки в коррелированных подзапросах
  • Неоптимальные решения, которые работают на малых объемах данных, но неэффективны в реальных условиях
  • Неправильная обработка NULL-значений в соединениях и сравнениях

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

Читайте также

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Что такое SQL?
1 / 5

Владимир Титов

редактор про сервисные сферы

Свежие материалы

Загрузка...