🗄️ Вопросы интервью по SQL

Вопросы 11-20 для разработчиков

Вопрос SQL 11
Объясните нормализацию и кратко опишите различные нормальные формы

Ответ: Нормализация организует реляционные данные для минимизации избыточности и предотвращения аномалий обновления/вставки/удаления путем разделения таблиц на основе зависимостей, сохраняя при этом смысл.

Нормальные формы:
  • 1NF (First Normal Form): Каждый столбец содержит атомарные значения, нет повторяющихся групп
  • 2NF (Second Normal Form): Соответствует 1NF и удаляет частичные зависимости от составного первичного ключа
  • 3NF (Third Normal Form): Соответствует 2NF и удаляет переходные зависимости
  • BCNF (Boyce-Codd Normal Form): Каждый детерминант должен быть кандидатным ключом
  • 4NF (Fourth Normal Form): Удаляет многозначные зависимости
  • 5NF/PJNF (Fifth Normal Form/Project-Join Normal Form): Удаляет зависимости join для избежания избыточности данных
Цель нормализации:
  • Минимизировать избыточность данных
  • Предотвратить аномалии обновления, вставки, удаления
  • Сохранить целостность данных
  • Оптимизировать производительность БД
Вопрос SQL 12
В чем разница между UNION и UNION ALL?
Сравнительная таблица:
UNION UNION ALL
Объединяет результаты из нескольких SELECT запросов и удаляет дублирующиеся строки Объединяет результаты из нескольких SELECT запросов и сохраняет все дублирующиеся строки
Выполняет DISTINCT операцию, поэтому может быть медленнее Не удаляет дубликаты, поэтому работает быстрее
Используется когда нужны уникальные результаты Используется когда нужны все результаты, включая дубликаты
Пример:

-- UNION - удаляет дубликаты
SELECT emp_name FROM employees WHERE salary > 50000
UNION
SELECT emp_name FROM employees WHERE dept_id = 10;

-- UNION ALL - сохраняет дубликаты
SELECT emp_name FROM employees WHERE salary > 50000
UNION ALL
SELECT emp_name FROM employees WHERE dept_id = 10;
                
Вопрос SQL 13
Как отличаются Clustered и Non-Clustered индексы?
Сравнительная таблица:
Clustered Index Non-Clustered Index
Хранит строки таблицы в физическом порядке ключа индекса Хранит индексные данные отдельно от таблицы со ссылками на строки
Только один кластерный индекс на таблицу Можно создать несколько некластерных индексов на таблице
Лучше для запросов диапазона и сортировки Лучше для фильтрации, объединений и быстрого поиска
Обычно создается на первичном ключе Обычно создается на часто ищущихся столбцах
Пример создания:

-- Clustered Index (на первичном ключе)
CREATE CLUSTERED INDEX idx_emp_id
ON employees(emp_id);

-- Non-Clustered Index (на часто ищущихся столбцах)
CREATE NONCLUSTERED INDEX idx_emp_name
ON employees(emp_name);

-- Составной Non-Clustered Index
CREATE NONCLUSTERED INDEX idx_emp_dept_salary
ON employees(dept_id, salary);
                
Вопрос SQL 14
Как выполняется сопоставление шаблонов в SQL?

Ответ: Сопоставление шаблонов в SQL выполняется с использованием оператора LIKE с подстановочными символами.

Подстановочные символы:
  • % - соответствует нулю или более символам
  • _ - соответствует ровно одному символу
Примеры LIKE:

-- Начинается с 'A'
SELECT * FROM employees WHERE emp_name LIKE 'A%';

-- Заканчивается на 'son'
SELECT * FROM employees WHERE emp_name LIKE '%son';

-- Содержит 'john' где-нибудь
SELECT * FROM employees WHERE emp_name LIKE '%john%';

-- Ровно 4 буквы
SELECT * FROM employees WHERE emp_name LIKE '____';

-- Начинается на J, заканчивается на n
SELECT * FROM employees WHERE emp_name LIKE 'J%n';

-- Вторая буква 'a'
SELECT * FROM employees WHERE emp_name LIKE '_a%';
                
Вопрос SQL 15
Как рассчитать бегущий итог продаж для каждого продукта?

Ответ: Бегущий итог рассчитывается с использованием оконной функции SUM() с клаузой OVER(). Это добавляет значение каждой строки к накопленному итогу, сохраняя отдельные строки.

Пример SQL:

SELECT
    product_id,
    sale_date,
    amount,
    SUM(amount) OVER (
        PARTITION BY product_id
        ORDER BY sale_date
    ) AS running_total
FROM sales;

-- Этот запрос вычисляет кумулятивный итог продаж для каждого продукта в хронологическом порядке
                
Пояснение:
  • SUM(amount): Суммирует значения
  • OVER (): Определяет оконную функцию
  • PARTITION BY product_id: Разделяет данные по продуктам
  • ORDER BY sale_date: Упорядочивает строки по дате продажи
  • AS running_total: Имя результирующего столбца
Другие оконные функции:
  • ROW_NUMBER() - номер строки
  • RANK() - ранг с пропусками
  • DENSE_RANK() - ранг без пропусков
  • LAG() - значение из предыдущей строки
  • LEAD() - значение из следующей строки
Вопрос SQL 16
Объясните correlated subqueries и предоставьте пример использования

Ответ: Correlated subquery — это подзапрос, который ссылается на столбцы из внешнего запроса. Он выполняется один раз для каждой строки, обработанной внешним запросом.

Характеристики:
  • Ссылается на столбцы из внешнего запроса
  • Выполняется один раз для каждой строки внешнего запроса
  • Может быть медленнее чем JOIN, но более гибкая
  • Часто используется для фильтрации на основе агрегатных функций
Пример:

Найти сотрудников, зарплата которых больше средней зарплаты их отдела:


SELECT e.employee_id, e.name, e.salary, e.department_id
FROM Employees e
WHERE e.salary > (
    SELECT AVG(e2.salary)
    FROM Employees e2
    WHERE e2.department_id = e.department_id
);
                
Пояснение:
  • Внешний запрос выбирает всех сотрудников
  • Для каждого сотрудника внутренний запрос вычисляет среднюю зарплату его отдела
  • Сравнивает зарплату сотрудника со средней зарплатой
  • e2.department_id = e.department_id - это связь с внешним запросом
Выход:

Возвращает сотрудников, зарплата которых выше средней зарплаты их отдела.

Вопрос SQL 17
Что такое EXISTS и NOT EXISTS и как они отличаются от IN?
Описание:
  • EXISTS: Возвращает TRUE если подзапрос возвращает хотя бы одну строку
  • NOT EXISTS: Возвращает TRUE если подзапрос не возвращает строк
  • IN: Проверяет существование значения в списке или результате подзапроса
Различия:
  • EXISTS и NOT EXISTS: Прекращают поиск как только найдена совпадающая строка и хорошо работают с correlated subqueries
  • IN: Сравнивает значение со всеми значениями, возвращенными подзапросом, и лучше подходит для небольших результирующих наборов
Примеры:

EXISTS - Найти клиентов, которые сделали хотя бы один заказ:


SELECT CustomerID
FROM Customers c
WHERE EXISTS (
    SELECT 1
    FROM Orders o
    WHERE o.CustomerID = c.CustomerID
);
                

NOT EXISTS - Найти клиентов, которые не разместили никакие заказы:


SELECT CustomerID
FROM Customers c
WHERE NOT EXISTS (
    SELECT 1
    FROM Orders o
    WHERE o.CustomerID = c.CustomerID
);
                

IN - Найти сотрудников, работающих в определенных отделах:


SELECT emp_name
FROM employees
WHERE dept_id IN (SELECT dept_id FROM departments WHERE location = 'NYC');
                
Выход EXISTS:

Возвращает клиентов, которые имеют хотя бы один заказ.

Вопрос SQL 18
Объясните anti-joins

Ответ: Anti-join возвращает строки из одной таблицы, которые не имеют совпадающих строк в другой таблице. Обычно реализуется с использованием NOT EXISTS или LEFT JOIN с IS NULL.

Характеристики:
  • Возвращает строки из одной таблицы, которые НЕ совпадают в другой
  • Противоположность INNER JOIN
  • Полезна для поиска отсутствующих данных
  • Может быть реализована двумя способами
Способ 1: Используя NOT EXISTS

SELECT c.CustomerID, c.CustomerName
FROM Customers c
WHERE NOT EXISTS (
    SELECT 1
    FROM Orders o
    WHERE o.CustomerID = c.CustomerID
);
                
Способ 2: Используя LEFT JOIN с IS NULL

SELECT c.CustomerID, c.CustomerName
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
WHERE o.CustomerID IS NULL;
                
Пояснение:
  • LEFT JOIN возвращает все строки из левой таблицы (Customers)
  • WHERE o.CustomerID IS NULL фильтрует только те строки, где совпадения не было
  • Результат — все клиенты, которые не разместили заказы
Выход:

Возвращает клиентов, которые не разместили никакие заказы.

Вопрос SQL 19
Объясните разницу между RANK(), DENSE_RANK() и ROW_NUMBER()
Сравнительная таблица:
ROW_NUMBER() RANK() DENSE_RANK()
Присваивает уникальный номер каждой строке Присваивает одинаковый ранг дублирующимся значениям Присваивает одинаковый ранг дублирующимся значениям
Дублирующиеся значения получают разные номера Дублирующиеся значения получают одинаковый ранг Дублирующиеся значения получают одинаковый ранг
Нет пропусков в нумерации Пропускает следующий ранг после дубликатов Не пропускает следующий ранг после дубликатов
Пример:

SELECT
    emp_name,
    salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
    RANK() OVER (ORDER BY salary DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;

-- Если у нас есть зарплаты: 100, 90, 90, 80
-- ROW_NUMBER():    1, 2, 3, 4
-- RANK():          1, 2, 2, 4 (пропускает 3)
-- DENSE_RANK():    1, 2, 2, 3 (не пропускает)
                
Когда использовать:
  • ROW_NUMBER(): Когда нужны уникальные номера для каждой строки
  • RANK(): Когда нужны ранги с пропусками (как спорт классификация)
  • DENSE_RANK(): Когда нужны ранги без пропусков
Вопрос SQL 20
Объясните назначение функций LAG и LEAD

Ответ: LAG и LEAD — это оконные функции, используемые для доступа к значениям из предыдущей или следующей строки без использования self-join.

Определения:
  • LAG(): Возвращает значение из предыдущей строки
  • LEAD(): Возвращает значение из следующей строки
Характеристики:
  • Обычно используются для сравнения последовательных строк
  • Вычисления разниц и анализа тенденций
  • Позволяют избежать self-join для улучшения производительности
  • Работают с PARTITION BY и ORDER BY
Синтаксис:

-- LAG - получить значение из предыдущей строки
LAG(column_name, offset, default_value) OVER (
    PARTITION BY partition_column
    ORDER BY order_column
)

-- LEAD - получить значение из следующей строки
LEAD(column_name, offset, default_value) OVER (
    PARTITION BY partition_column
    ORDER BY order_column
)
                
Примеры:

Пример 1: Сравнение продаж текущего месяца с предыдущим:


SELECT
    month,
    sales,
    LAG(sales, 1, 0) OVER (ORDER BY month) AS prev_month_sales,
    sales - LAG(sales, 1, 0) OVER (ORDER BY month) AS sales_difference
FROM monthly_sales;
                

Пример 2: Найти следующую зарплату сотрудника:


SELECT
    emp_name,
    salary,
    LEAD(salary) OVER (ORDER BY emp_id) AS next_emp_salary
FROM employees;
                

Пример 3: Анализ тенденций по отделам:


SELECT
    dept_id,
    emp_name,
    salary,
    LAG(salary) OVER (PARTITION BY dept_id ORDER BY salary) AS prev_salary,
    LEAD(salary) OVER (PARTITION BY dept_id ORDER BY salary) AS next_salary
FROM employees;
                
Параметры:
  • column_name: Столбец для получения значения
  • offset: Количество строк назад/вперед (по умолчанию 1)
  • default_value: Значение если нет предыдущей/следующей строки
Использование:
  • Расчет изменений (день за днем, месяц за месяцем)
  • Анализ последовательностей
  • Сравнение смежных значений
  • Расчет перехода между статусами