S
SQL Portal | Базы Данных
20.09.2026 16:07 · 👁 1.1K
SQL-вопрос:
Что вернёт этот запрос?
👉 @SQLPortal
S
SQL Portal | Базы Данных
20.09.2026 06:07 · 👁 1.1K
Проверка уникальности работает через =, а NULL = NULL не даёт true. По умолчанию NULL не считается уникальным значением.
Postgres не может доказать равенство двух NULL, поэтому пропускает обе строки.
Простой пример, где это ломается, — составной ключ:
-- UNIQUE (user_id, org_id, deleted_at)
INSERT INTO memberships VALUES (1, 10, NULL);
INSERT INTO memberships VALUES (1, 10, NULL);
-- обе строки будут вставлены
В итоге можно получить две активные записи membership для одного и того же пользователя, потому что ограничение уникальности вообще не срабатывает.
NULLS NOT DISTINCT
Начиная с Postgres 15, уникальные ограничения и индексы могут считать NULL равными:
UNIQUE NULLS NOT DISTINCT (user_id, org_id, deleted_at)
Второй INSERT уже завершится ошибкой.
Но это также запретит две строки с одинаковым значением deleted_at, а не только две активные строки.
Частичный уникальный индекс
Если уникальность нужно обеспечить только для активных строк, используйте partial unique index:
CREATE UNIQUE INDEX ON memberships (user_id, org_id)
WHERE deleted_at IS NULL;
👉 @SQLPortal
S
SQL Portal | Базы Данных
19.09.2026 16:07 · 👁 987
SQL-фишка, о которой знают далеко не все
А вы знали, что внутри transaction можно создавать контрольные точки с помощью SAVEPOINT? Обычный ROLLBACK отменяет всю transaction. А SAVEPOINT позволяет создать точку, к которой можно откатиться, отменив только изменения, сделанные после неё.
Без SAVEPOINT:
BEGIN;
INSERT INTO orders VALUES (1, 'Laptop');
INSERT INTO payments VALUES (1, 'FAILED');
-- Что-то пошло не так
ROLLBACK;
В этом случае будут отменены и заказ, и платёж.
С SAVEPOINT:
BEGIN;
INSERT INTO orders VALUES (1, 'Laptop');
SAVEPOINT before_payment;
INSERT INTO payments VALUES (1, 'FAILED');
-- Платёж не прошёл
ROLLBACK TO SAVEPOINT before_payment;
-- Пробуем другой способ оплаты
INSERT INTO payments VALUES (1, 'SUCCESS');
COMMIT;
Теперь можно откатить только неудачную часть transaction и продолжить работу с последней безопасной точки.
SAVEPOINT особенно полезен в сложных операциях, где полный откат и повторное выполнение всей transaction обходятся слишком дорого.
SAVEPOINT не предотвращает ошибку. Он просто помогает не откатывать всю работу из-за одной неудачной операции.
👉 @SQLPortal
S
SQL Portal | Базы Данных
19.09.2026 10:30 · 👁 999
SQL-вопрос:
Какой запрос вы бы выполнили?
👉 @SQLPortal
S
SQL Portal | Базы Данных
19.09.2026 06:07 · 👁 1K
SQL-вопрос:
Какой запрос находит клиентов, которые сделали хотя бы один заказ?
👉 @SQLPortal
S
SQL Portal | Базы Данных
18.09.2026 16:07 · 👁 1.1K
ОЧЕНЬ ПЛОХАЯ ПРИВЫЧКА
Зачем вообще использовать номера позиций столбцов в GROUP BY?
Ну серьёзно, ЗАЧЕМ, ЗАЧЕМ, ЗАЧЕМ? 😬
По-моему, это просто ленивый способ писать SQL-код. Всё, я это сказал.
Убедите меня, что я неправ.
👉 @SQLPortal
S
SQL Portal | Базы Данных
18.09.2026 06:07 · 👁 1.1K
SQL-вопрос: Интересно, как вы ответите на этот вопрос.
Вы анализируете входы пользователей в систему и хотите узнать общее количество входов для каждого пользователя, но только для тех, кто входил более 5 раз. Вы пишете запрос:
Ну что, знатоки SQL, погнали!
👉 @SQLPortal
S
SQL Portal | Базы Данных
17.09.2026 16:07 · 👁 1.1K
SQL-вопрос: Какой запрос возвращает только premium-пользователей?
👉 @SQLPortal
S
SQL Portal | Базы Данных
17.09.2026 06:07 · 👁 1.2K
Большой бесплатный гайд по SQL для начинающих
На freeCodeCamp лежит полноценный текстовый курс по SQL, который ведёт от самых основ до более серьёзных тем. Материал построен последовательно и подойдёт тем, кто только начинает разбираться в реляционных базах данных.
Внутри: таблицы и типы данных, CRUD, SELECT и WHERE, constraints, агрегатные функции, подзапросы, нормализация, JOIN и основы оптимизации запросов. Для практики в курсе используется SQLite, при этом отдельно разбираются различия между SQL-базами.
Хороший материал, чтобы системно пройти SQL с нуля или освежить базу.
👉 @SQLPortal
S
SQL Portal | Базы Данных
16.09.2026 16:07 · 👁 1.1K
Я написал два почти одинаковых SQL-запроса.
Один оказался в 451 раз быстрее. Я реализовывал cursor pagination и добавил composite index, чтобы ускорить запрос. Но вместо этого он стал медленнее.
Query plan показывал Index Scan — так в чём же была проблема? Дело было не в размере dataset. Проблема заключалась в том, как сравнивались значения cursor.
После перехода на tuple comparison PostgreSQL смог эффективно использовать composite index. Время выполнения: 0,668 мс.
Та же логика pagination, но совершенно другой query plan.
Без проверки query plan эту проблему можно было бы легко пропустить.
👉 @SQLPortal