SQL Ready | Базы Данных
18.1K subscribers
1.39K photos
119 videos
2 files
798 links
Авторский канал про Базы Данных и SQL
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Cотрудничество: @energy_c

Реклама на бирже: https://telega.in/channels/sql_ready/card

РКН: https://clck.ru/3QREBc
Download Telegram
🖥 Oracle - ограничение количества строк и выборка Top-N!

Получение первых N строк с FETCH FIRST, постраничная выборка с FETCH NEXT и OFFSET, включение строк с одинаковыми значениями сортировки через WITH TIES, ограничение результата в процентах с PERCENT, использование ROWNUM, а также нумерация и ранжирование с ROW_NUMBER() и DENSE_RANK().

➡️ SQL Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍11❤6
This media is not supported in your browser
VIEW IN TELEGRAM
😎 SQLinfo — русскоязычный справочник по SQL!

Сайт с учебными и справочными материалами по SQL и работе с реляционными базами данных. Подойдёт для изучения основных конструкций языка, понимания принципов построения запросов и повторения теории. Ресурс пригодится начинающим аналитикам и разработчикам, а также тем, кому нужен дополнительный справочник по SQL во время обучения или работы с базами данных.

📌 Оставляю ссылочку: sqlinfo.ru

➡️ SQL Ready | #сайт
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥9❤7
Почему BETWEEN может незаметно исключать данные при фильтрации по датам!

При работе с временными диапазонами часто используют 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, не пересекаются на границах и удобны для отчётности, пакетной обработки и инкрементальной загрузки данных.

➡️ SQL Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13❤6👍5
📂 Шпаргалка по Eventual Consistency в распределённых системах!

Например, Event-Driven обеспечивает асинхронное распространение изменений между сервисами, а CQRS позволяет разделять операции чтения и записи с независимым обновлением моделей данных.

На картинке — 4 архитектурных подхода, которые помогают управлять согласованностью данных, снижать связанность компонентов и масштабировать распределённые системы.

Сохрани, чтобы не потерять!

➡️ SQL Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤7👍4
Переносите строки без промежуточной выборки!

Обычно архивирование делают в два этапа: сначала копируют записи, затем удаляют исходные. В 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 позволяет переносить строки между таблицами одним атомарным запросом, без отдельного чтения и передачи данных через приложение.

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤8👍8
👍 Интересная статья лежит на Хабре: «За пределами EXPLAIN: как увидеть выполнение запроса вживую в распределённой СУБД»!

В этой статье:
• Узнаете, почему EXPLAIN не всегда помогает понять причины медленного выполнения SQL-запросов;
• Разберёте, как pg_query_state позволяет отслеживать выполнение запросов в Apache Cloudberry в реальном времени;
• Посмотрите, как архитектура с Go-агентами и сервисом YAGPCC помогает собирать метрики с множества узлов без перегрузки координатора.

🔊 Продолжай читать на Habr!


➡️ SQL Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🤝7🔥4
Почему перенос условия из ON в WHERE меняет семантику LEFT JOIN

При использовании 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 оптимизатором.

➡️ SQL Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13❤8👍7
📂 16 паттернов программирования, которые должен знать каждый разработчик!

Например, Two Pointers помогает эффективно работать с массивами, Sliding Window — находить подмассивы по заданным условиям, а Dynamic Programming — решать сложные задачи через повторное использование результатов подзадач.

На картинке — 16 популярных алгоритмических паттернов и структур данных, которые пригодятся на технических собеседованиях.

Сохрани, чтобы не потерять!

➡️ SQL Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤5🤝4🔥3
Ускоряйте массовую загрузку через FREEZE!

При загрузке данных в новую таблицу PostgreSQL может сразу пометить строки как замороженные. Для этого у COPY есть малоизвестная опция FREEZE.
BEGIN;

CREATE TABLE events_import (
id bigint,
payload jsonb
);

COPY events_import
FROM '/data/events.csv'
WITH (FORMAT csv, FREEZE true);

COMMIT;


FREEZE позволяет избежать последующей работы по заморозке загруженных строк. Но есть условие: таблица должна быть создана или очищена через TRUNCATE в текущей подоперации транзакции.

Опция пригодится и при повторной полной загрузке таблицы:
BEGIN;

TRUNCATE events_import;

COPY events_import
FROM '/data/events.csv'
WITH (FORMAT csv, FREEZE true);

COMMIT;


FREEZE не отключает обычные проверки ограничений и не гарантирует ускорения самого импорта. Его преимущество — уменьшение будущей работы VACUUM по заморозке строк.

🔥 Используйте COPY FREEZE при полной загрузке новых или очищенных таблиц, чтобы сократить последующую работу по обслуживанию данных.

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥9❤4🤝4
This media is not supported in your browser
VIEW IN TELEGRAM
😎 Индексы в PostgreSQL — подробное руководство по оптимизации SQL-запросов!

Статья о том, как работают индексы в PostgreSQL, зачем они нужны и в каких случаях помогают ускорить выполнение запросов. Материал объясняет принципы индексирования и влияние индексов на поиск, фильтрацию и обработку данных. Отдельное внимание уделяется ситуациям, когда индексы не используются или могут снижать производительность.

📌 Оставляю ссылочку: sqllab.ru

➡️ SQL Ready | #сайт
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤7🔥3🤝2
Удаляйте данные небольшими порциями!

В PostgreSQL у DELETE нет LIMIT. Но ограничить количество удаляемых строк можно через подзапрос.
DELETE FROM events
WHERE id IN (
SELECT id
FROM events
WHERE created_at < now() - interval '90 days'
LIMIT 1000
);


Такой запрос удалит не более 1000 строк за один запуск. Это позволяет очищать большие таблицы постепенно, не создавая одну огромную транзакцию.

Если очистку запускают несколько процессов, можно добавить блокировку с пропуском уже занятых строк:
DELETE FROM events
WHERE id IN (
SELECT id
FROM events
WHERE created_at < now() - interval '90 days'
FOR UPDATE SKIP LOCKED
LIMIT 1000
);


Каждый запуск должен выполняться в отдельной транзакции. При повторении запроса таблица постепенно очищается без необходимости удалять всё сразу.

🔥 DELETE через ограниченный подзапрос позволяет удалять данные порциями, сокращая длительность транзакций и нагрузку от массовой очистки.

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15👍8🔥4🤝1
📂 Как работает Git: наглядная шпаргалка!

Основные команды Git и схема взаимодействия между рабочей директорией, индексом (staging area), локальным и удалённым репозиториями.

На схеме также показано, как работают git fetch, git pull и другие команды для управления изменениями и синхронизации репозиториев.

Сохрани, чтобы не потерять!

➡️ SQL Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍6🔥3🤝1
Почему ON CONFLICT DO NOTHING может скрывать ошибки в данных

В PostgreSQL ON CONFLICT DO NOTHING позволяет пропускать вставку строк при нарушении уникальности. Однако без указания конкретной цели конфликта PostgreSQL учитывает все подходящие уникальные ограничения и индексы. Из-за этого операция может быть пропущена по причине, которую бизнес-логика приложения не предусматривала.

Допустим, есть таблица пользователей:
CREATE TABLE users (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
username TEXT NOT NULL UNIQUE
);


Добавим существующего пользователя:
INSERT INTO users (email, username)
VALUES ('alex@example.com', 'alex');


Теперь приложение пытается зарегистрировать другого пользователя:
INSERT INTO users (email, username)
VALUES ('maria@example.com', 'alex')
ON CONFLICT DO NOTHING;


Запрос завершится без ошибки, но новая запись не появится. Причина в том, что ON CONFLICT DO NOTHING без указания цели конфликта обрабатывает нарушения уникальности по всем подходящим уникальным ограничениям и индексам.

В данном случае email уникален, но username = 'alex' уже существует. Если приложение проверяет только успешность выполнения SQL-запроса, оно может ошибочно считать регистрацию завершённой.

Чтобы обрабатывать только ожидаемый конфликт, следует явно указать его цель:
INSERT INTO users (email, username)
VALUES ('maria@example.com', 'alex')
ON CONFLICT (email) DO NOTHING;


Теперь PostgreSQL пропустит вставку только при конфликте по email. Нарушение уникальности username приведёт к ошибке 23505 (unique_violation).

Дополнительно можно использовать RETURNING:
INSERT INTO users (email, username)
VALUES ('alex@example.com', 'alex2')
ON CONFLICT (email) DO NOTHING
RETURNING id;


Если запись вставлена, запрос вернёт её id. Если вставка пропущена из-за конфликта по email, результат будет пустым. Это позволяет приложению отличать успешную вставку от пропущенной операции без дополнительного SELECT. Важно учитывать, что RETURNING при DO NOTHING не возвращает существующую строку и не сообщает причину конфликта.

🔥 При использовании ON CONFLICT DO NOTHING важно явно указывать цель конфликта, если бизнес-логика допускает дубликаты только по определённому ключу. В сочетании с RETURNING это позволяет контролировать результат вставки, не скрывая нарушения уникальности по другим ключам.

➡️ SQL Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤7🔥5
🖥 MySQL — операторы и функции сравнения данных!

Определение максимального и минимального значений с GREATEST() и LEAST(), лексикографическое сравнение строк через STRCMP(), NULL-безопасное сравнение с <=>, проверка принадлежности диапазону с BETWEEN и NOT BETWEEN, поиск совпадений в наборе значений через IN, а также сопоставление строк с шаблонами с помощью LIKE.

➡️ SQL Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥6🤝6👍1
Обновляйте агрегаты одним запросом!

Когда нужно пересчитать статистику по пользователям, не обязательно сначала получать агрегаты, а затем обновлять каждую запись отдельно.

PostgreSQL позволяет объединить INSERT ... SELECT с ON CONFLICT:
INSERT INTO user_stats (user_id, total)
SELECT user_id, sum(amount)
FROM payments
GROUP BY user_id
ON CONFLICT (user_id)
DO UPDATE
SET total = EXCLUDED.total;


Здесь GROUP BY вычисляет суммы, а ON CONFLICT обновляет существующие записи или добавляет новые.

Для этого на user_stats(user_id) должно быть ограничение уникальности. Можно сразу пересчитывать несколько показателей:
INSERT INTO user_stats (user_id, total, orders_count)
SELECT user_id, sum(amount), count(*)
FROM payments
GROUP BY user_id
ON CONFLICT (user_id)
DO UPDATE SET
total = EXCLUDED.total,
orders_count = EXCLUDED.orders_count;


🔥 INSERT ... SELECT ... ON CONFLICT позволяет пересчитывать и сохранять агрегаты одним запросом, без циклов и промежуточной обработки в приложении.

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤5🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
😍 Продвинутая оптимизация PostgreSQL — WAL, VACUUM и проектирование БД!

Статья о том, как повысить производительность PostgreSQL за счёт правильной настройки внутренних механизмов СУБД и проектирования структуры базы данных. Материал посвящён не только оптимизации SQL-запросов, но и причинам возникновения проблем с производительностью. Рассматриваются WAL (журнал предзаписи), VACUUM и AUTOVACUUM, очистка устаревших версий строк, управление транзакциями и проектирование таблиц.

📌 Оставляю ссылочку: dbserv.ru

➡️ SQL Ready | #сайт
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤6🔥6