PostgreSQL и MySQL — два самых популярных реляционных SQL-движка. Их сравнивают на собеседованиях, о них спорят на форумах. В этой статье — конкретные отличия с примерами, а не абстрактное «зависит от задачи».
Коротко о каждом
MySQL — создан в 1995 году. Долгое время был стандартом для веб-стека (LAMP). Сейчас принадлежит Oracle. Используется в WordPress, Shopify, Twitter (исторически).
PostgreSQL — создан в 1996 году как academic project UC Berkeley. Open source без корпоративного владельца. Используется в Instagram, Reddit, Gitlab, Notion.
Соответствие стандарту SQL
PostgreSQL строже следует стандарту SQL:92/99/2003. MySQL исторически допускал нестандартное поведение ради производительности.
-- В MySQL (без ONLY_FULL_GROUP_BY) это работает, хотя нарушает стандарт:
SELECT user_id, name, COUNT(*)
FROM orders
GROUP BY user_id;
-- name не агрегирована и не в GROUP BY — PostgreSQL выдаст ошибку
-- PostgreSQL заставит написать правильно:
SELECT user_id, MAX(name), COUNT(*)
FROM orders
GROUP BY user_id;
В MySQL 5.7+ режим
ONLY_FULL_GROUP_BYвключён по умолчанию — это исправляет поведение, но старый код может сломаться.
Типы данных
PostgreSQL значительно богаче по типам:
| Возможность | PostgreSQL | MySQL |
|---|---|---|
| JSON / JSONB | ✅ полноценный | ✅ ограниченный |
Массивы (int[]) | ✅ | ❌ |
Диапазоны (daterange) | ✅ | ❌ |
| UUID как тип | ✅ нативный | ⚠️ только VARCHAR |
| Enum | ✅ | ✅ |
| HSTORE (ключ-значение) | ✅ | ❌ |
| Геометрические типы | ✅ PostGIS | ⚠️ базовые |
-- PostgreSQL: массивы прямо в колонке
CREATE TABLE tags_example (
id SERIAL PRIMARY KEY,
tags TEXT[]
);
INSERT INTO tags_example (tags) VALUES (ARRAY['sql', 'postgresql', 'tutorial']);
SELECT * FROM tags_example WHERE 'sql' = ANY(tags);
-- PostgreSQL: диапазоны
SELECT * FROM events
WHERE event_period && '[2026-01-01, 2026-03-31]'::daterange;
JSON: принципиальная разница
Оба поддерживают JSON, но PostgreSQL делает это лучше.
-- PostgreSQL: JSONB — бинарное хранение, индексирование, полный поиск
CREATE TABLE orders (
id SERIAL,
metadata JSONB
);
CREATE INDEX ON orders USING GIN(metadata);
-- Быстрый поиск по вложенному полю:
SELECT * FROM orders WHERE metadata @> '{"source": "mobile"}';
-- Извлечение поля:
SELECT metadata->>'city' AS city FROM orders;
-- MySQL: JSON поддерживается, но без GIN-индексов
SELECT JSON_EXTRACT(metadata, '$.city') FROM orders;
Оконные функции
PostgreSQL поддерживает все оконные функции стандарта SQL с 2009 года. MySQL добавил их только в версии 8.0 (2018).
-- Нарастающий итог — работает одинаково в обоих (MySQL 8+)
SELECT
order_date,
amount,
SUM(amount) OVER (ORDER BY order_date) AS running_total
FROM orders;
-- Но PostgreSQL поддерживает больше frame-опций:
SUM(amount) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW -- скользящее за 7 дней
)
Транзакции и MVCC
Оба используют MVCC (Multi-Version Concurrency Control), но реализация разная.
PostgreSQL:
- DDL (ALTER TABLE, CREATE INDEX) — транзакционны. Можно откатить.
- Уровень изоляции по умолчанию:
READ COMMITTED REPEATABLE READв PostgreSQL сильнее стандарта — защищает от фантомных чтений
MySQL (InnoDB):
- DDL — НЕ транзакционны.
ALTER TABLEнельзя откатить. - Уровень изоляции по умолчанию:
REPEATABLE READ
-- PostgreSQL: можно откатить CREATE TABLE
BEGIN;
CREATE TABLE test (id INT);
INSERT INTO test VALUES (1);
ROLLBACK; -- таблица не создана
-- MySQL: такого не получится — DDL автоматически коммитит
FULL OUTER JOIN
MySQL не поддерживает FULL OUTER JOIN. Нужна эмуляция через UNION.
-- PostgreSQL — нативно:
SELECT u.name, o.id
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
-- MySQL — только так:
SELECT u.name, o.id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
UNION
SELECT u.name, o.id
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
Производительность: миф о MySQL
Раньше MySQL считался быстрее для простых SELECT. Сейчас это не так:
- На сложных аналитических запросах PostgreSQL обычно быстрее
- Для высоконагруженных OLTP-систем (миллионы простых запросов в секунду) MySQL всё ещё конкурентоспособен
- PostgreSQL лучше работает с параллельными запросами и большими JOIN
- Для аналитики и аггрегаций — PostgreSQL выигрывает
Что выбрать
PostgreSQL — если:
- Аналитика, сложные запросы, JOIN нескольких таблиц
- Нужны JSON, массивы, диапазоны
- Важна строгость SQL и транзакционность DDL
- Проект новый (нет legacy)
MySQL — если:
- Унаследованный проект уже на MySQL
- Простое веб-приложение (CMS, блог, e-commerce)
- Команда знает MySQL лучше
На большинстве новых проектов в 2026 году выбирают PostgreSQL. Если нет специфических требований — идите на Postgres.
Хочешь закрепить знания? Все задачи в нашем тренажёре работают на PostgreSQL — том самом, который используется в продакшене.