SQL Portal | Базы Данных
13.7K subscribers
1.11K photos
142 videos
51 files
801 links
Присоединяйтесь к нашему каналу и погрузитесь в мир баз данных

Связь: @devmangx

РКН: https://clck.ru/3H4Wo3
Download Telegram
Загружать 100 000 строк, чтобы посчитать одну итоговую сумму, — странный способ защитить базу данных от лишней работы.

SQL и сам прекрасно умеет считать суммы.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6💊1
SQL-вопрос: Будьте внимательны — здесь есть подвох.

Что вернёт этот запрос и почему?

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Варианты:
Anonymous Quiz
33%
A
24%
B
19%
C
24%
D
Главное правило SQL GROUP BY, которое должен знать каждый

Если в SELECT используются неагрегированные столбцы, убедитесь, что они добавлены в GROUP BY. Это не просто хорошая практика — в некоторых базах данных это обязательное требование.

Почему это важно?

GROUP BY определяет, что представляет собой одна строка результата. База данных должна точно понимать, какое единственное значение поместить в каждую ячейку этой строки.

В запросе ниже одна строка = одна должность (job_title) внутри конкретного отдела (department). Поэтому и department, и job_title должны присутствовать в GROUP BY.

Если пропустить неагрегированный столбец:

• Некоторые СУБД вернут ошибку
• Другие могут вернуть произвольное значение
• Запрос может «работать» сегодня, но завтра вернуть некорректный результат

Общее правило

Каждый столбец в SELECT должен быть функционально зависим от GROUP BY. Если внутри одной группы столбец может содержать несколько значений, его нужно либо добавить в GROUP BY, либо использовать с агрегатной функцией.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4💊1
Я написал два почти одинаковых 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
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍2
Большой бесплатный гайд по SQL для начинающих

На freeCodeCamp лежит полноценный текстовый курс по SQL, который ведёт от самых основ до более серьёзных тем. Материал построен последовательно и подойдёт тем, кто только начинает разбираться в реляционных базах данных.

Внутри: таблицы и типы данных, CRUD, SELECT и WHERE, constraints, агрегатные функции, подзапросы, нормализация, JOIN и основы оптимизации запросов. Для практики в курсе используется SQLite, при этом отдельно разбираются различия между SQL-базами.

Хороший материал, чтобы системно пройти SQL с нуля или освежить базу.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
SQL-вопрос: Какой запрос возвращает только premium-пользователей?

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Варианты:
Anonymous Quiz
44%
A
3%
B
2%
C
51%
D
SQL-вопрос: Интересно, как вы ответите на этот вопрос.

Вы анализируете входы пользователей в систему и хотите узнать общее количество входов для каждого пользователя, но только для тех, кто входил более 5 раз. Вы пишете запрос:

Ну что, знатоки SQL, погнали!

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Варианты:
Anonymous Quiz
14%
A
56%
B
14%
C
16%
D
ОЧЕНЬ ПЛОХАЯ ПРИВЫЧКА

Зачем вообще использовать номера позиций столбцов в GROUP BY?

Ну серьёзно, ЗАЧЕМ, ЗАЧЕМ, ЗАЧЕМ? 😬
По-моему, это просто ленивый способ писать SQL-код. Всё, я это сказал.

Убедите меня, что я неправ.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3😁2💊1
SQL-вопрос:

Какой запрос находит клиентов, которые сделали хотя бы один заказ?

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Варианты:
Anonymous Quiz
32%
A
18%
B
6%
C
43%
D
SQL-вопрос:

Какой запрос вы бы выполнили?

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Варианты:
Anonymous Quiz
37%
A
63%
B
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Проверка уникальности работает через =, а 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
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🔥2
SQL-вопрос:

Что вернёт этот запрос?

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Варианты:
Anonymous Quiz
49%
A
8%
B
3%
C
39%
D
❤3👍1
Самый чистый способ получить последнюю запись для каждого пользователя

Можно фильтровать результаты window functions, таких как ROW_NUMBER() или RANK(), напрямую — без дополнительного subquery или CTE.

Вместо:
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY timestamp DESC
) AS rn
FROM events
)
SELECT *
FROM ranked
WHERE rn = 1;


Можно написать:
SELECT *
FROM events
QUALIFY ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY timestamp DESC
) = 1;


QUALIFY
особенно удобен для deduplication, выбора последних записей и очистки данных. Он заметно уменьшает вложенность запросов и делает SQL проще для чтения. Только не забудьте проверить, поддерживает ли ваша СУБД QUALIFY.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
SQL-вопрос:

Что вернёт этот запрос?

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM