SQL шпаргалка для начинающих: базовые запросы, JOIN, GROUP BY, подзапросы
Эта шпаргалка — для тех, кто начинает учить SQL или периодически забывает синтаксис. Здесь только база — JOIN, GROUP BY, подзапросы — то, что используется каждый день. Для продвинутых тем (оконные функции, CTE, сравнение диалектов) — отдельная шпаргалка по продвинутому SQL.
SELECT — получение данных
-- Выбрать всё
SELECT * FROM users;
-- Выбрать конкретные столбцы
SELECT name, email, created_at FROM users;
-- Убрать дубликаты
SELECT DISTINCT city FROM users;
-- Псевдонимы (алиасы)
SELECT name AS full_name, email AS contact FROM users;
-- Вычисляемые поля
SELECT name, salary * 12 AS annual_salary FROM employees;
-- Ограничение числа строк
SELECT * FROM users LIMIT 10;
SELECT * FROM users LIMIT 10 OFFSET 20; -- строки 21–30
-- В MySQL: LIMIT 20, 10 (смещение первым!)
WHERE — фильтрация
-- Операторы сравнения: =, !=, <>, <, >, <=, >=
SELECT * FROM products WHERE price > 100;
-- AND / OR / NOT
SELECT * FROM users WHERE age >= 18 AND country = 'RU';
SELECT * FROM users WHERE country = 'RU' OR country = 'BY';
SELECT * FROM users WHERE NOT status = 'deleted';
-- BETWEEN — диапазон
SELECT * FROM orders WHERE amount BETWEEN 100 AND 500;
-- IN — список значений
SELECT * FROM users WHERE country IN ('RU', 'BY', 'KZ');
-- LIKE — поиск по шаблону (% — любые символы, _ — один символ)
SELECT * FROM users WHERE name LIKE 'А%'; -- начинается на А
SELECT * FROM users WHERE email LIKE '%@gmail%'; -- содержит gmail
SELECT * FROM users WHERE phone LIKE '+7___'; -- +7 и три цифры после
-- IS NULL / IS NOT NULL
SELECT * FROM users WHERE phone IS NULL;
SELECT * FROM users WHERE phone IS NOT NULL;
ORDER BY — сортировка
-- По одному полю
SELECT * FROM products ORDER BY price; -- по возрастанию
SELECT * FROM products ORDER BY price DESC; -- по убыванию
-- По нескольким полям
SELECT * FROM orders ORDER BY customer_id, order_date DESC;
-- С LIMIT — топ-N
SELECT * FROM products ORDER BY sales_count DESC LIMIT 10;
-- Случайный порядок (разный синтаксис!)
-- PostgreSQL: ORDER BY RANDOM()
-- MySQL: ORDER BY RAND()
-- SQLite: ORDER BY RANDOM()
-- Oracle: ORDER BY DBMS_RANDOM.VALUE
Агрегатные функции и GROUP BY
-- Агрегатные функции
SELECT COUNT(*) FROM orders; -- количество строк
SELECT COUNT(DISTINCT customer_id) FROM orders; -- уникальных клиентов
SELECT SUM(amount) FROM orders; -- сумма
SELECT AVG(amount) FROM orders; -- среднее
SELECT MIN(amount), MAX(amount) FROM orders; -- минимум, максимум
-- GROUP BY — группировка
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;
-- Группировка по нескольким полям
SELECT customer_id, status, SUM(amount) AS total
FROM orders
GROUP BY customer_id, status;
-- HAVING — фильтрация после группировки (WHERE — до)
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 1000;
-- Полный порядок запроса:
-- SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ...
JOIN — соединение таблиц
-- INNER JOIN — только совпадающие строки
SELECT u.name, o.amount, o.order_date
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- LEFT JOIN — все строки из левой таблицы
SELECT u.name, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- Вернёт ВСЕХ пользователей, даже без заказов (o.amount будет NULL)
-- RIGHT JOIN — все строки из правой
SELECT u.name, o.amount
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- Вернёт ВСЕ заказы, даже если пользователь удалён
-- FULL OUTER JOIN — все строки из обеих таблиц
SELECT u.name, o.amount
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
-- JOIN нескольких таблиц
SELECT u.name, o.id AS order_id, p.name AS product, oi.quantity
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id;
-- SELF JOIN — соединение таблицы с собой (иерархия сотрудников)
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
-- CROSS JOIN — декартово произведение
SELECT * FROM colors CROSS JOIN sizes;
-- JOIN с дополнительными условиями
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id AND o.status = 'paid';
Подзапросы
-- В WHERE — скалярный подзапрос
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
-- В WHERE — с IN (подзапрос возвращает список)
SELECT name
FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);
-- В WHERE — с EXISTS (проверка существования)
SELECT name
FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-- В SELECT — скалярный подзапрос
SELECT name,
salary,
(SELECT AVG(salary) FROM employees) AS avg_salary,
salary - (SELECT AVG(salary) FROM employees) AS diff
FROM employees;
-- В FROM — производная таблица
SELECT dept, avg_sal
FROM (
SELECT department AS dept, AVG(salary) AS avg_sal
FROM employees
GROUP BY department
) AS dept_stats
WHERE avg_sal > 100000;
-- Коррелированный подзапрос (ссылается на внешний запрос)
SELECT name, salary
FROM employees e1
WHERE salary > (SELECT AVG(salary) FROM employees e2 WHERE e2.department = e1.department);
UNION — объединение результатов
-- UNION — убирает дубликаты
SELECT name, email FROM customers
UNION
SELECT name, email FROM suppliers;
-- UNION ALL — сохраняет все строки (быстрее)
SELECT name FROM active_users
UNION ALL
SELECT name FROM archived_users;
-- Правила: одинаковое число столбцов, совместимые типы, ORDER BY в конце
SELECT name, 'клиент' AS type FROM customers
UNION
SELECT name, 'поставщик' FROM suppliers
ORDER BY name;
INSERT, UPDATE, DELETE — изменение данных
-- INSERT — вставка строк
INSERT INTO users (name, email, age) VALUES ('Alice', 'alice@ex.com', 30);
INSERT INTO users (name, email, age) VALUES
('Bob', 'bob@ex.com', 25),
('Carol', 'carol@ex.com', 28);
-- INSERT из SELECT
INSERT INTO premium_users (id, name)
SELECT id, name FROM users WHERE total_orders > 50;
-- UPDATE — обновление
UPDATE users SET status = 'active' WHERE id = 42;
UPDATE users SET salary = salary * 1.1 WHERE department = 'Engineering';
-- DELETE — удаление
DELETE FROM users WHERE status = 'deleted' AND last_login < '2025-01-01';
-- TRUNCATE — очистка всей таблицы (быстрее DELETE без WHERE)
TRUNCATE TABLE temp_logs;
CREATE, ALTER, DROP — DDL
-- Создание таблицы
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE,
age INTEGER CHECK (age >= 0),
role VARCHAR(50) DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Внешний ключ
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Автоинкремент (разный синтаксис!)
-- PostgreSQL: id SERIAL PRIMARY KEY
-- MySQL: id INT AUTO_INCREMENT PRIMARY KEY
-- SQLite: id INTEGER PRIMARY KEY (автоматически!)
-- Oracle: id NUMBER GENERATED AS IDENTITY
-- ALTER TABLE — изменение структуры
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;
ALTER TABLE users RENAME COLUMN name TO full_name;
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
-- Индексы
CREATE INDEX idx_users_email ON users(email);
CREATE UNIQUE INDEX idx_users_phone ON users(phone);
DROP INDEX idx_users_email;
-- Удаление таблицы
DROP TABLE IF EXISTS temp_data;
Основные встроенные функции
-- Строковые
SELECT UPPER(name), LOWER(email) FROM users;
SELECT LENGTH(name) FROM users; -- MySQL: байты! лучше CHAR_LENGTH()
SELECT SUBSTRING(name FROM 1 FOR 3) FROM users;
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;
SELECT TRIM(name) FROM users; -- убрать пробелы по краям
SELECT REPLACE(description, 'old', 'new') FROM products;
-- Числовые
SELECT ROUND(price, 2), CEIL(price), FLOOR(price) FROM products;
SELECT ABS(balance), POWER(amount, 2), SQRT(value) FROM data;
-- Дата и время (различаются!)
SELECT CURRENT_DATE; -- сегодня
SELECT CURRENT_TIMESTAMP; -- сейчас
SELECT EXTRACT(YEAR FROM created_at) FROM users;
SELECT DATE_PART('month', created_at) FROM users; -- PostgreSQL
SELECT MONTH(created_at) FROM users; -- MySQL
-- Преобразование типов
SELECT CAST(amount AS INTEGER) FROM orders;
SELECT amount::INTEGER FROM orders; -- PostgreSQL
SELECT amount::VARCHAR FROM orders; -- PostgreSQL
-- COALESCE — первое не-NULL значение
SELECT name, COALESCE(phone, 'не указан') AS phone FROM users;
-- NULLIF — вернуть NULL если значения равны
SELECT NULLIF(dividend, 0) FROM data; -- избежать деления на 0
-- CASE — условная логика
SELECT name,
CASE
WHEN salary < 50000 THEN 'low'
WHEN salary < 100000 THEN 'medium'
ELSE 'high'
END AS salary_level
FROM employees;
Транзакции
-- Явная транзакция
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;
COMMIT; -- или ROLLBACK при ошибке
-- Точка сохранения
BEGIN;
UPDATE orders SET status = 'shipped' WHERE id = 42;
SAVEPOINT shipped;
UPDATE orders SET status = 'delivered' WHERE id = 42;
-- Можно откатить к точке: ROLLBACK TO shipped;
COMMIT;
Таблица частых ошибок
| Ошибка | Пример | Как исправить |
|---|---|---|
= вместо == |
WHERE name == 'Alice' |
WHERE name = 'Alice' |
| Одинарные кавычки для строк | WHERE name = "Alice" |
WHERE name = 'Alice' |
| Путаница WHERE и HAVING | HAVING amount > 100 (без GROUP BY) |
WHERE amount > 100 |
NULL = NULL |
WHERE phone = NULL |
WHERE phone IS NULL |
| Группировка без агрегации | SELECT name, COUNT(*) FROM users |
SELECT name, COUNT(*) FROM users GROUP BY name |
LIMIT без ORDER BY |
SELECT * FROM t LIMIT 10 |
SELECT * FROM t ORDER BY id LIMIT 10 |
| Таблица в GROUP BY, поле в SELECT — не то же | SELECT department, AVG(salary) FROM employees |
GROUP BY department |
| Забыт алиас для подзапроса в FROM | FROM (SELECT ...) |
FROM (SELECT ...) AS t |
Что дальше
База освоена. Дальше — продвинутые темы:
- SQL для продвинутых: оконные функции, CTE, JSON, сравнение диалектов
- Шпаргалка по Python: от основ до профи
- Шпаргалка по Pandas: DataFrame, groupby
- Шпаргалка Regex: регулярные выражения
- Шпаргалка по Git: основные команды
- Data Science инструменты: стек, roadmap, с чего начать
Связанные статьи: SQL для продвинутых, 50+ инструментов разработчика 2025, Шпаргалка Docker: команды и Dockerfile