Получение первых N строк с FETCH FIRST, постраничная выборка с FETCH NEXT и OFFSET, включение строк с одинаковыми значениями сортировки через WITH TIES, ограничение результата в процентах с PERCENT, использование ROWNUM, а также нумерация и ранжирование с ROW_NUMBER() и DENSE_RANK(). Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍11❤6
This media is not supported in your browser
VIEW IN TELEGRAM
Сайт с учебными и справочными материалами по SQL и работе с реляционными базами данных. Подойдёт для изучения основных конструкций языка, понимания принципов построения запросов и повторения теории. Ресурс пригодится начинающим аналитикам и разработчикам, а также тем, кому нужен дополнительный справочник по SQL во время обучения или работы с базами данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥9❤6
Почему BETWEEN может незаметно исключать данные при фильтрации по датам!
При работе с временными диапазонами часто используют
Допустим, есть таблица заказов:
Нужно получить заказы за сентябрь 2026 года. На первый взгляд подходит запрос:
Однако
Например, запись со временем
Но это тоже ненадёжный вариант. Если колонка поддерживает дробные доли секунды, запись
Для временных интервалов практичнее использовать полуоткрытый диапазон:
Такой запрос включает все значения за сентябрь независимо от точности хранения времени и исключает записи следующего месяца.
Ещё одно преимущество — соседние интервалы не пересекаются. Например, при последовательной обработке данных можно использовать:
При условии, что следующая выборка начинается с предыдущего
Такой подход также хорошо сочетается с обычным B-tree индексом:
Поскольку фильтрация выполняется непосредственно по индексируемой колонке, оптимизатор может использовать диапазонное сканирование индекса. При использовании типов с часовыми поясами границы необходимо задавать в соответствии с нужной временной зоной, особенно если речь идёт о календарных днях.
🔥 Для фильтрации по временным интервалам условия
➡️ SQL Ready | #практика
При работе с временными диапазонами часто используют
BETWEEN. Но если колонка хранит дату вместе со временем, такой фильтр может возвращать не все ожидаемые записи.Допустим, есть таблица заказов:
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
created_at TIMESTAMP NOT NULL,
amount DECIMAL(12, 2)
);
Нужно получить заказы за сентябрь 2026 года. На первый взгляд подходит запрос:
SELECT *
FROM orders
WHERE created_at BETWEEN
TIMESTAMP '2026-09-01 00:00:00'
AND TIMESTAMP '2026-09-30 00:00:00';
Однако
BETWEEN включает обе границы диапазона и эквивалентен сочетанию >= и <=. Поэтому запрос вернёт записи с 1 сентября по начало 30 сентября, но исключит все заказы, созданные позднее полуночи последнего дня месяца.Например, запись со временем
2026-09-30 15:30:00 не попадёт в результат. Иногда верхнюю границу пытаются исправить вручную:WHERE created_at BETWEEN
TIMESTAMP '2026-09-01 00:00:00'
AND TIMESTAMP '2026-09-30 23:59:59';
Но это тоже ненадёжный вариант. Если колонка поддерживает дробные доли секунды, запись
2026-09-30 23:59:59.500 окажется за пределами диапазона.Для временных интервалов практичнее использовать полуоткрытый диапазон:
SELECT *
FROM orders
WHERE created_at >= TIMESTAMP '2026-09-01 00:00:00'
AND created_at < TIMESTAMP '2026-10-01 00:00:00';
Такой запрос включает все значения за сентябрь независимо от точности хранения времени и исключает записи следующего месяца.
Ещё одно преимущество — соседние интервалы не пересекаются. Например, при последовательной обработке данных можно использовать:
WHERE created_at >= :date_from
AND created_at < :date_to;
При условии, что следующая выборка начинается с предыдущего
date_to, каждая запись на границе интервалов попадёт ровно в один диапазон.Такой подход также хорошо сочетается с обычным B-tree индексом:
CREATE INDEX idx_orders_created_at
ON orders (created_at);
Поскольку фильтрация выполняется непосредственно по индексируемой колонке, оптимизатор может использовать диапазонное сканирование индекса. При использовании типов с часовыми поясами границы необходимо задавать в соответствии с нужной временной зоной, особенно если речь идёт о календарных днях.
>= и < обычно надёжнее, чем BETWEEN. Полуоткрытые диапазоны не зависят от точности TIMESTAMP, не пересекаются на границах и удобны для отчётности, пакетной обработки и инкрементальной загрузки данных.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤6👍4
Например, Event-Driven обеспечивает асинхронное распространение изменений между сервисами, а CQRS позволяет разделять операции чтения и записи с независимым обновлением моделей данных.
На картинке — 4 архитектурных подхода, которые помогают управлять согласованностью данных, снижать связанность компонентов и масштабировать распределённые системы.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤7👍4
Требуются 7 человек, чтобы помогали собирать чат-ботов для бизнеса по блокам, как лего.
Стоит попробовать, если хочешь:
✅ ЗП: от 5000-9000₽ за 1-2 вечера.
✅ Занятость: 3–4 часа в день.
✅ Свободный график.
Опыт в разработке не требуется.
Всё, что нужно для старта — запустить обучение
👉@pautov_techspec_bot
И пройти инструктаж.
Стоит попробовать, если хочешь:
✅ ЗП: от 5000-9000₽ за 1-2 вечера.
✅ Занятость: 3–4 часа в день.
✅ Свободный график.
Опыт в разработке не требуется.
Всё, что нужно для старта — запустить обучение
👉@pautov_techspec_bot
И пройти инструктаж.
👎1
Переносите строки без промежуточной выборки!
Обычно архивирование делают в два этапа: сначала копируют записи, затем удаляют исходные. В PostgreSQL можно связать эти действия через
А теперь используем возвращённые строки как источник для вставки:
Всё выполняется одним оператором SQL. Если вставка в архив завершится ошибкой, удаление тоже откатится.
При этом можно преобразовать данные перед архивированием:
🔥
➡️ SQL Ready | #совет
Обычно архивирование делают в два этапа: сначала копируют записи, затем удаляют исходные. В PostgreSQL можно связать эти действия через
DELETE ... RETURNING.-- Удаляем строки и сразу получаем их содержимое
DELETE FROM events
WHERE created_at < DATE '2025-01-01'
RETURNING id, user_id, payload, created_at;
А теперь используем возвращённые строки как источник для вставки:
WITH moved AS (
DELETE FROM events
WHERE created_at < DATE '2025-01-01'
RETURNING id, user_id, payload, created_at
)
INSERT INTO events_archive (
id, user_id, payload, created_at
)
SELECT id, user_id, payload, created_at
FROM moved;
Всё выполняется одним оператором SQL. Если вставка в архив завершится ошибкой, удаление тоже откатится.
При этом можно преобразовать данные перед архивированием:
WITH moved AS (
DELETE FROM events
WHERE created_at < DATE '2025-01-01'
RETURNING id, payload, created_at
)
INSERT INTO events_archive (id, payload, archived_at)
SELECT id, payload, now()
FROM moved;
DELETE ... RETURNING внутри WITH позволяет переносить строки между таблицами одним атомарным запросом, без отдельного чтения и передачи данных через приложение.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤7👍6
В этой статье:
• Узнаете, почему EXPLAIN не всегда помогает понять причины медленного выполнения SQL-запросов;• Разберёте, как pg_query_state позволяет отслеживать выполнение запросов в Apache Cloudberry в реальном времени;• Посмотрите, как архитектура с Go-агентами и сервисом YAGPCC помогает собирать метрики с множества узлов без перегрузки координатора.🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝6❤5🔥2
Вот отсортированная база с тонной материала (постепенно пополняется):
(363 видео, 87 книги) — Python
(117 видео, 53 книги) — ВАЙБКОД
(174 видео, 91 книги) — DevOps
(415 видео, 68 книги) — Frontend
(137 видео, 93 книги) — DS/ML
(143 видео, 33 книги) — ИБ/Хакинг
(352 видео, 89 книги) — С/С++/C
(343 видео, 87 книги) — Java/QA
(176 видео, 32 книги) — Git/Linux
(167 видео, 53 книги) — PHP/1С
(227 видео, 83 книги) — SQL/БД
(114 видео, 77 книги) — Сисадмин
(107 видео, 43 книги) — BA/SA
(181 видео, 32 книги) — Go/Rust
(167 видео, 43 книги) — Kotlin/Swift
(112 видео, 24 книги) — Flutter
(113 видео, 82 книги) — GameDev
(183 видео, 37 книги) — Дизайн
(136 видео, 33 книги) — PM/HR
Скачивать ничего не нужно — все выложили в Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Почему перенос условия из ON в WHERE меняет семантику LEFT JOIN
При использовании
Рассмотрим запрос, который должен возвращать всех пользователей и количество их завершённых заказов:
Этот запрос исключает пользователей без завершённых заказов. Предикат
Для сохранения всех пользователей ограничение на статус необходимо включить в условие соединения:
Теперь пользователь без завершённых заказов остаётся в результате с количеством
В PostgreSQL аналогичную задачу можно решить через агрегатный
Различие между этими вариантами становится существенным при вычислении нескольких агрегатов.
Есть и важное следствие для оптимизатора PostgreSQL. Если условие в
Проверить фактический план выполнения можно через
В плане может появиться
🔥 Перенос предиката между
➡️ SQL Ready | #практика
При использовании
LEFT JOIN расположение предиката определяет не только состав результата, но и возможность сохранить строки левой таблицы без соответствий. Предикаты в ON участвуют в поиске совпадений, тогда как WHERE фильтрует результат соединения.Рассмотрим запрос, который должен возвращать всех пользователей и количество их завершённых заказов:
SELECT
u.id,
COUNT(o.id) AS completed_orders
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
WHERE o.status = 'completed'
GROUP BY u.id;
Этот запрос исключает пользователей без завершённых заказов. Предикат
o.status = 'completed' является null-rejecting: он не может вернуть TRUE, когда o.status содержит NULL. Поэтому строки, дополненные NULL при внешнем соединении, отбрасываются.Для сохранения всех пользователей ограничение на статус необходимо включить в условие соединения:
SELECT
u.id,
COUNT(o.id) AS completed_orders
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
AND o.status = 'completed'
GROUP BY u.id;
Теперь пользователь без завершённых заказов остаётся в результате с количеством
0. Здесь принципиально использование COUNT(o.id): агрегат игнорирует NULL, тогда как COUNT(*) учитывает и строку, созданную внешним соединением при отсутствии совпадений.В PostgreSQL аналогичную задачу можно решить через агрегатный
FILTER, сохранив соединение со всеми заказами:SELECT
u.id,
COUNT(o.id) FILTER (
WHERE o.status = 'completed'
) AS completed_orders
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
GROUP BY u.id;
Различие между этими вариантами становится существенным при вычислении нескольких агрегатов.
FILTER позволяет применять независимые условия к разным агрегатам без повторного соединения таблиц:SELECT
u.id,
COUNT(o.id) FILTER (
WHERE o.status = 'completed'
) AS completed_orders,
COUNT(o.id) FILTER (
WHERE o.status = 'pending'
) AS pending_orders
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
GROUP BY u.id;
Есть и важное следствие для оптимизатора PostgreSQL. Если условие в
WHERE исключает все строки с NULL со стороны правой таблицы, внешнее соединение в некоторых случаях может быть преобразовано во внутреннее. Такое преобразование сохраняет семантику исходного запроса, но не исправляет ошибочно выбранную логику фильтрации.Проверить фактический план выполнения можно через
EXPLAIN:EXPLAIN (ANALYZE, BUFFERS)
SELECT
u.id,
COUNT(o.id)
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
WHERE o.status = 'completed'
GROUP BY u.id;
В плане может появиться
Hash Join вместо Hash Left Join или другой тип внутреннего соединения. Конкретный алгоритм зависит от статистики, индексов и оценок стоимости, поэтому наличие LEFT JOIN в sql не гарантирует сохранение внешнего соединения в физическом плане.ON и WHERE при внешнем соединении — семантическое преобразование, а не просто изменение записи sql. Null-rejecting предикаты могут устранять строки без совпадений и создавать условия для упрощения LEFT JOIN до INNER JOIN оптимизатором.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7❤6👍3