Вопросы 21-30 для разработчиков
| CROSS JOIN | INNER JOIN |
|---|---|
| Возвращает декартово произведение обеих таблиц | Возвращает только совпадающие строки на основе условия join |
| Не требует условия join | Требует условие join с использованием ON клаузы |
| Каждая строка первой таблицы объединяется с каждой строкой второй таблицы | Только строки, которые удовлетворяют условию join, возвращаются |
| Если Таблица A имеет 3 строки и Таблица B имеет 4 строки, результат содержит 12 строк (3 × 4) | Количество строк зависит от совпадающих записей между таблицами |
-- CROSS JOIN - все комбинации
SELECT c.customer_id, p.product_id
FROM customers c
CROSS JOIN products p;
-- Если 5 клиентов и 10 продуктов = 50 строк
-- INNER JOIN - только совпадения
SELECT c.customer_id, o.order_id
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;
-- Возвращает только клиентов с заказами
Ответ: Foreign Key — это столбец (или набор столбцов) в одной таблице, который ссылается на первичный ключ другой таблицы. Он обеспечивает ссылочную целостность, гарантируя, что значения в дочерней таблице существуют в родительской таблице, предотвращая невалидные или orphan записи.
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
-- С действиями ON DELETE/UPDATE
CREATE TABLE order_items (
item_id INT PRIMARY KEY,
order_id INT,
product_id INT,
FOREIGN KEY (order_id) REFERENCES orders(order_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
| Операция | Назначение | Использование |
|---|---|---|
| UNION | Объединяет результирующие наборы и удаляет дублирующиеся строки | Объединение данных из нескольких таблиц, сохраняя уникальные записи |
| INTERSECT | Возвращает только строки, общие для обоих результирующих наборов | Найти записи, которые существуют в обеих таблицах |
| EXCEPT (MINUS в Oracle) | Возвращает строки из первого запроса, которые не существуют во втором | Найти записи, присутствующие в одной таблице, но отсутствующие в другой |
UNION - Объединение сотрудников и клиентов:
SELECT name FROM employees
UNION
SELECT name FROM customers;
-- Возвращает уникальные имена из обеих таблиц
INTERSECT - Люди, которые являются и сотрудниками, и клиентами:
SELECT name FROM employees
INTERSECT
SELECT name FROM customers;
-- Возвращает только имена, которые есть в обеих таблицах
EXCEPT - Сотрудники, которые не являются клиентами:
SELECT name FROM employees
EXCEPT
SELECT name FROM customers;
-- Возвращает имена из employees, которые НЕ в customers
-- ❌ МЕДЛЕННЫЙ запрос
SELECT * FROM employees
WHERE UPPER(emp_name) = 'JOHN'
AND YEAR(hire_date) = 2020;
-- ✅ ОПТИМИЗИРОВАННЫЙ запрос
SELECT emp_id, emp_name, salary
FROM employees
WHERE emp_name = 'John'
AND hire_date >= '2020-01-01' AND hire_date < '2021-01-01';
-- Добавить индекс на emp_name и hire_date
CREATE INDEX idx_emp_name_hire_date ON employees(emp_name, hire_date);
Ответ: Database partitioning — это процесс разделения большой таблицы на меньшие, управляемые партиции, сохраняя при этом её как единую логическую таблицу. Это улучшает производительность запросов, упрощает обслуживание и повышает масштабируемость.
Range Partitioning (по дате):
CREATE TABLE sales (
sale_id INT,
sale_date DATE,
amount DECIMAL(10,2)
)
PARTITION BY RANGE (YEAR(sale_date)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
List Partitioning (по списку):
CREATE TABLE customers (
customer_id INT,
region VARCHAR(20)
)
PARTITION BY LIST (region) (
PARTITION p_north VALUES IN ('North', 'Northeast'),
PARTITION p_south VALUES IN ('South', 'Southeast'),
PARTITION p_west VALUES IN ('West', 'Southwest')
);
Ответ: SQL injection можно предотвратить, следуя этим best practices:
❌ НЕБЕЗОПАСНО - Конкатенация строк:
-- ОПАСНО! SQL Injection возможен
String query = "SELECT * FROM users WHERE username = '" + username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
✅ БЕЗОПАСНО - Параметризованный запрос:
-- Безопасно используя prepared statement
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
-- Если пользователь вводит:
username = "admin' OR '1'='1"
-- Небезопасный запрос становится:
SELECT * FROM users WHERE username = 'admin' OR '1'='1'
-- Это вернет всех пользователей!
-- С параметризованным запросом это не сработает
Ответ: SQL команды широко классифицируются на четыре основных типа:
-- DDL - Определение структуры
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(100)
);
-- DML - Работа с данными
INSERT INTO employees VALUES (1, 'John');
SELECT * FROM employees;
UPDATE employees SET emp_name = 'Jane' WHERE emp_id = 1;
DELETE FROM employees WHERE emp_id = 1;
-- DCL - Контроль доступа
GRANT SELECT ON employees TO user1;
REVOKE DELETE ON employees FROM user2;
-- TCL - Управление транзакциями
BEGIN TRANSACTION;
UPDATE employees SET salary = 50000 WHERE emp_id = 1;
COMMIT; -- Сохранить
-- ROLLBACK; -- Отменить
Ответ: Ограничение DEFAULT присваивает значение по умолчанию столбцу, когда никакое значение не предоставляется при INSERT операции. Это помогает поддерживать согласованность данных и упрощает ввод данных.
-- DEFAULT с константой
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(100),
hire_date DATE DEFAULT GETDATE(), -- текущая дата
status VARCHAR(20) DEFAULT 'Active', -- текстовое значение
salary DECIMAL(10,2) DEFAULT 30000 -- числовое значение
);
-- Вставка без указания default столбцов
INSERT INTO employees (emp_id, emp_name)
VALUES (1, 'John');
-- hire_date будет текущая дата
-- status будет 'Active'
-- salary будет 30000
-- Добавление DEFAULT к существующему столбцу
ALTER TABLE employees
ADD CONSTRAINT df_status DEFAULT 'Inactive' FOR status;
DEFAULT GETDATE() - текущая дата и времяDEFAULT CURRENT_TIMESTAMP - текущий timestampDEFAULT 0 - числовое значениеDEFAULT 'N/A' - текстовое значениеDEFAULT NEWID() - новый GUID (SQL Server)Ответ: Denormalization — это процесс объединения нормализованных таблиц в большие таблицы для улучшения производительности. Она используется когда сложные запросы и JOIN замедляют получение данных, и преимущества производительности перевешивают недостатки избыточности.
| Нормализация | Denormalization |
|---|---|
| Разделяет данные для минимизации избыточности | Объединяет данные для улучшения производительности |
| Снижает размер базы данных | Увеличивает размер базы данных |
| Требует больше JOIN операций | Требует меньше JOIN операций |
| Упрощает UPDATE/DELETE операции | Усложняет UPDATE/DELETE операции |
Нормализованная структура:
-- Таблица заказов (Order) ссылается на Customer
SELECT o.order_id, o.order_date, c.customer_name, c.city
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;
-- Требует JOIN = медленнее для больших объемов данных
Denormalized структура:
-- Заказы содержат данные клиента (дублирование)
CREATE TABLE orders_denormalized (
order_id INT,
order_date DATE,
customer_id INT,
customer_name VARCHAR(100), -- Дублировано из customers
city VARCHAR(50) -- Дублировано из customers
);
SELECT * FROM orders_denormalized
WHERE city = 'New York';
-- Без JOIN = быстрее!
SELECT salary + bonus AS total_comp FROM employees;
SELECT price * quantity AS total FROM products;
SELECT * FROM employees WHERE salary > 50000;
SELECT * FROM orders WHERE order_date <> '2026-01-01';
SELECT * FROM employees
WHERE salary > 50000 AND dept_id = 10;
SELECT * FROM orders
WHERE status = 'Pending' OR status = 'Processing';
SELECT * FROM employees WHERE NOT status = 'Inactive';
SELECT emp_name FROM employees
UNION
SELECT cust_name FROM customers;
-- BETWEEN
SELECT * FROM sales WHERE amount BETWEEN 100 AND 500;
-- IN
SELECT * FROM employees WHERE dept_id IN (10, 20, 30);
-- LIKE
SELECT * FROM employees WHERE emp_name LIKE 'J%';
-- IS NULL
SELECT * FROM orders WHERE shipping_date IS NULL;
-- Oracle/PostgreSQL
SELECT first_name || ' ' || last_name AS full_name FROM employees;
-- SQL Server
SELECT first_name + ' ' + last_name AS full_name FROM employees;
-- Комбинация нескольких операторов
SELECT emp_name, salary
FROM employees
WHERE salary > 40000 -- Comparison
AND (dept_id = 10 OR dept_id = 20) -- Logical + Comparison
AND emp_name LIKE 'J%' -- Special
AND salary * 1.1 > 50000 -- Arithmetic
ORDER BY salary DESC;