Серия вопросов для интервью по SQL - 1-10
SQL (Structured Query Language) — это стандартный язык, используемый с реляционными СУБД для определения схем (DDL), манипулирования и запросов данных (DML/DQL) и оптимизации доступа. Это основной навык для разработчиков, аналитиков данных и администраторов БД, и это часто встречается на собеседованиях.
Вместе эти возможности обеспечивают эффективное хранение, получение и управление данными в системах реляционных баз данных.
| CHAR | VARCHAR2 |
|---|---|
| CHAR хранит данные символов фиксированной длины | VARCHAR2 хранит данные символов переменной длины |
| Заполняет неиспользуемое место пробелами в конце | Не заполняет неиспользуемое место, экономя память |
VIEW — это виртуальная таблица, созданная из SELECT запроса, которая отображает данные из одной или нескольких таблиц без их хранения, помогая упростить запросы и улучшить безопасность.
Ограничение UNIQUE гарантирует, что все значения в столбце (или комбинации столбцов) различны. Это предотвращает дублирующиеся значения и помогает поддерживать целостность данных.
Query — это SQL операция, используемая для получения, обновления или манипулирования данными в базе данных. Самый распространенный тип запроса — это SELECT statement, который извлекает данные из одной или нескольких таблиц на основе указанных условий.
Subquery — это запрос, вложенный в другой запрос. Он часто используется в WHERE клаузе для фильтрации данных на основе результатов другого запроса, делая его проще обработать сложные условия.
Composite primary key использует два или более столбца вместе для уникальной идентификации каждой строки, когда один столбец один не является достаточным.
WHERE фильтрует отдельные строки перед группировкой или агрегацией, так что он не может использовать агрегатные функции, такие как SUM или COUNT.
HAVING фильтрует результирующие группы после GROUP BY, поэтому он предназначен для условий на агрегаты (например, группы с итогом выше порога).
SELECT customer_id, COUNT(*) AS orders_2025
FROM orders
WHERE order_date >= '2025-01-01' AND order_date < '2026-01-01'
GROUP BY customer_id
HAVING COUNT(*) > 5;
Ответ: SQL joins объединяют строки из двух таблиц на основе условия сопоставления (обычно ключи) для ответа на вопросы, которые охватывают обе таблицы.
-- INNER JOIN - только совпадающие строки
SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
-- LEFT JOIN - все из левой + совпадения из правой
SELECT e.emp_name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;
-- RIGHT JOIN - все из правой + совпадения из левой
SELECT e.emp_name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
-- FULL JOIN - все из обеих таблиц
SELECT e.emp_name, d.dept_name
FROM employees e
FULL JOIN departments d ON e.dept_id = d.dept_id;
| Тип Join | Результат |
|---|---|
| INNER JOIN | Только строки, найденные в обеих таблицах |
| LEFT JOIN | Все строки из левой таблицы + совпадения из правой (с NULL где нет совпадения) |
| RIGHT JOIN | Все строки из правой таблицы + совпадения из левой (с NULL где нет совпадения) |
| FULL JOIN | Все строки из обеих таблиц (с NULL где отсутствует совпадение) |
Ответ: PRIMARY KEY и UNIQUE key оба обеспечивают уникальность, но имеют разные характеристики:
| Характеристика | PRIMARY KEY | UNIQUE KEY |
|---|---|---|
| Уникальность | Да (обязательно) | Да (обязательно) |
| NULL значения | НЕ разрешены | Разрешены (несколько) |
| Количество на таблицу | Только 1 | Может быть несколько |
| Использование в Foreign Key | По умолчанию да | Возможно, но реже |
| Индекс | Создается автоматически | Создается автоматически |
CREATE TABLE users (
user_id INT PRIMARY KEY, -- PRIMARY KEY (не NULL, уникален)
email VARCHAR(100) UNIQUE, -- UNIQUE KEY (может быть NULL, уникален)
username VARCHAR(50) UNIQUE, -- UNIQUE KEY
phone VARCHAR(20)
);
Ответ: CTE (Common Table Expression) — это временный именованный результирующий набор, созданный с использованием клаузы WITH, который существует только во время выполнения одного SQL запроса. Используется для упрощения сложных запросов, повышения читаемости, избегания повторения подзапросов и написания рекурсивных запросов.
WITH cte_name AS (
SELECT column1, column2, ...
FROM table_name
WHERE condition
)
SELECT * FROM cte_name;
-- Простая CTE для получения высокооплачиваемых сотрудников
WITH high_earners AS (
SELECT emp_id, emp_name, salary
FROM employees
WHERE salary > 100000
)
SELECT * FROM high_earners;
-- Несколько CTE вместе
WITH dept_avg_salary AS (
SELECT dept_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept_id
),
high_earners AS (
SELECT e.emp_name, e.salary, d.avg_salary
FROM employees e
JOIN dept_avg_salary d ON e.dept_id = d.dept_id
WHERE e.salary > d.avg_salary
)
SELECT * FROM high_earners;
-- Рекурсивная CTE (пример иерархии)
WITH RECURSIVE employee_hierarchy AS (
-- Base case: все сотрудники без менеджера
SELECT emp_id, emp_name, manager_id, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- Recursive case: связать с подчиненными
SELECT e.emp_id, e.emp_name, e.manager_id, eh.level + 1
FROM employees e
JOIN employee_hierarchy eh ON e.manager_id = eh.emp_id
)
SELECT * FROM employee_hierarchy;