В этой статье:
• Узнаете, как ускорять сложные SQL-запросы с помощью условной агрегации;• Разберёте применение CASE и FILTER вместо множества подзапросов и JOIN-ов;• Посмотрите на примерах, как сделать SQL-код быстрее, чище и проще для поддержки.🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤6🔥6🤝2
7,2 млн рублей призового фонда и реальные задачи космической отрасли 🚀
В сентябре пройдет серия КосмоХакатонов для студентов, молодых ученых и специалистов.
Участникам предстоит за два дня разработать собственное решение, поработать с экспертами и представить проект жюри.
Участников ждут:
🛰 реальные задачи космической отрасли
👥 команды от 3 до 5 человек
💻 очный и онлайн-форматы
🧑💻 работа с экспертами и трекерами
🏆 финал в Москве
Первый КосмоХакатон стартует уже 4 сентября в Ростове-на-Дону.
Дальше серия продолжится в Красноярске, Нижнем Новгороде, Благовещенске и Санкт-Петербурге, а завершится финалом в Москве.
Участие бесплатное. Присоединиться могут студенты, аспиранты, молодые ученые и специалисты от 18 лет.
🔗 Зарегистрироваться: https://космохакатон.рф
В сентябре пройдет серия КосмоХакатонов для студентов, молодых ученых и специалистов.
Участникам предстоит за два дня разработать собственное решение, поработать с экспертами и представить проект жюри.
Участников ждут:
🛰 реальные задачи космической отрасли
👥 команды от 3 до 5 человек
💻 очный и онлайн-форматы
🧑💻 работа с экспертами и трекерами
🏆 финал в Москве
Первый КосмоХакатон стартует уже 4 сентября в Ростове-на-Дону.
Дальше серия продолжится в Красноярске, Нижнем Новгороде, Благовещенске и Санкт-Петербурге, а завершится финалом в Москве.
Участие бесплатное. Присоединиться могут студенты, аспиранты, молодые ученые и специалисты от 18 лет.
🔗 Зарегистрироваться: https://космохакатон.рф
Почему ctid нельзя использовать как постоянный идентификатор!
В PostgreSQL каждая строка имеет системную колонку
Иногда
Для начала создадим простую таблицу с первичным ключом и несколькими тестовыми записями, чтобы наглядно проследить, как изменяется значение
Теперь добавим несколько строк, с которыми будем работать в дальнейших примерах.
Посмотрим, какое значение
Результат может выглядеть так:
Теперь изменим одну из строк. На первый взгляд кажется, что PostgreSQL просто обновит существующую запись, однако механизм хранения данных работает иначе.
После выполнения
Теперь можно увидеть, что значение
Это происходит из-за механизма MVCC. При выполнении
Изменение
или
Поэтому использовать
При этом
Например, создадим отдельную таблицу без ограничений уникальности:
Добавим одинаковые строки:
Удалим только одну из двух одинаковых строк:
В результате останется одна строка с именем
🔥
➡️ SQL Ready | #практика
В PostgreSQL каждая строка имеет системную колонку
ctid. Она содержит физический адрес текущей версии строки в таблице: номер страницы и позицию строки внутри этой страницы.Иногда
ctid используют для поиска, удаления или диагностики отдельных записей, но важно понимать, что это не постоянный идентификатор строки. Значение ctid может измениться в процессе обычной работы базы данных, поэтому использовать его в прикладной логике нельзя.Для начала создадим простую таблицу с первичным ключом и несколькими тестовыми записями, чтобы наглядно проследить, как изменяется значение
ctid.CREATE TABLE users (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT
);
Теперь добавим несколько строк, с которыми будем работать в дальнейших примерах.
INSERT INTO users (name)
VALUES
('Alice'),
('Bob'),
('Charlie');
Посмотрим, какое значение
ctid PostgreSQL присвоил каждой записи после вставки.SELECT
ctid,
id,
name
FROM users;
Результат может выглядеть так:
ctid | id | name
------+----+--------
(0,1) | 1 | Alice
(0,2) | 2 | Bob
(0,3) | 3 | Charlie
Теперь изменим одну из строк. На первый взгляд кажется, что PostgreSQL просто обновит существующую запись, однако механизм хранения данных работает иначе.
UPDATE users
SET name = 'Robert'
WHERE id = 2;
После выполнения
UPDATE снова посмотрим значения ctid и сравним их с предыдущим результатом.SELECT
ctid,
id,
name
FROM users;
Теперь можно увидеть, что значение
ctid изменилось. Например:ctid | id | name
------+----+---------
(0,1) | 1 | Alice
(0,4) | 2 | Robert
(0,3) | 3 | Charlie
Это происходит из-за механизма MVCC. При выполнении
UPDATE PostgreSQL не изменяет строку на месте, а создаёт её новую версию, которая получает новый физический адрес (ctid). Старая версия строки некоторое время остаётся в таблице и может быть видима другим транзакциям в зависимости от их снимка данных. Изменение
ctid происходит не только при UPDATE. Любые операции, которые физически переписывают таблицу, также приводят к изменению физических адресов строк. Например:VACUUM FULL users;
или
CLUSTER users USING users_pkey;
Поэтому использовать
ctid в качестве внешнего ключа, хранить его в приложении или считать постоянным идентификатором записи нельзя.При этом
ctid остаётся полезным инструментом для служебных задач. Один из самых распространённых случаев — удалить одну запись среди полностью одинаковых дубликатов, когда значения всех пользовательских столбцов совпадают и отличить строки обычными средствами невозможно.Например, создадим отдельную таблицу без ограничений уникальности:
CREATE TABLE duplicate_users (
name TEXT
);
Добавим одинаковые строки:
INSERT INTO duplicate_users (name)
VALUES
('Alice'),
('Alice'),
('Bob');
Удалим только одну из двух одинаковых строк:
DELETE
FROM duplicate_users
WHERE ctid = (
SELECT ctid
FROM duplicate_users
WHERE name = 'Alice'
LIMIT 1
);
В результате останется одна строка с именем
Alice. В этом примере ctid определяется и используется в рамках одного SQL-запроса, поэтому используется актуальный физический адрес версии строки и не возникает проблемы с использованием устаревшего значения.ctid — это физический адрес текущей версии строки, а не её постоянный идентификатор. Для связи данных всегда используйте первичный ключ, а ctid оставьте для диагностических и служебных операций.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤6🔥5
Шпаргалка по обработке NULL в MySQL: подстановка резервных значений, проверка наличия и отсутствия данных, условная обработка, безопасные вычисления и сравнение nullable-значений. Помогает учитывать семантику NULL и избегать неочевидного поведения в условиях, выражениях и вычислениях.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13🔥7👍5😁2
This media is not supported in your browser
VIEW IN TELEGRAM
Сайт позволяет изучать SQL с нуля и сразу закреплять материал на практике. Бесплатный курс включает 10 уроков и 92 задачи с автоматической проверкой прямо в браузере. После базового курса можно перейти к SQL-тренажёру с сотнями задач повышенной сложности и заданиями в формате собеседований.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13❤8🤝5
Например,
Cache позволяет отдавать часто используемые данные без обращения к базе данных, а Load Balancer распределяет запросы между несколькими серверами, повышая скорость и отказоустойчивость приложения.На картинке — основные стратегии уменьшения задержек в высоконагруженных системах.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12🔥9🤝4👍1
Почему OFFSET деградирует на больших таблицах!
Есть таблица:
Типичная пагинация:
Кажется, что база должна просто пропустить миллион строк и вернуть следующие 50. Но SQL-движок не может сделать настоящий прыжок через
Без подходящего индекса это может выглядеть так: база читает строки, определяет порядок сортировки, обрабатывает
Даже если есть индекс:
ситуация всё равно не идеальна.
Индекс помогает быстрее читать данные в нужном порядке, но базе всё равно приходится пройти большое количество записей, чтобы добраться до нужного
Например:
означает, что базе нужно обработать примерно миллион строк перед тем, как вернуть результат.
Для глубоких страниц обычно используют keyset pagination (cursor pagination). Вместо номера страницы передаётся последнее значение из предыдущего результата:
Теперь база может использовать индекс и сразу искать позицию, с которой нужно продолжить чтение.
Но одного
Например:
Порядок становится неоднозначным. Поэтому добавляют уникальный ключ:
И запрос становится:
Теперь курсор точно определяет позицию в индексе.
Главное преимущество такого подхода — стоимость запроса зависит в основном от размера страницы, а не от глубины пагинации. Первая страница:
и страница после миллионов записей:
используют один и тот же принцип: найти позицию в B-tree индексе и продолжить чтение.
🔥 Делаем вывод:
➡️ SQL Ready | #практика
OFFSET часто используют для пагинации, потому что запрос выглядит просто. На небольших таблицах проблем обычно нет, но на больших объёмах стоимость такого подхода растёт вместе с глубиной страницы.Есть таблица:
events(
id BIGINT,
user_id BIGINT,
payload JSONB,
created_at TIMESTAMPTZ
)
Типичная пагинация:
SELECT
id,
user_id,
created_at
FROM events
ORDER BY created_at DESC
LIMIT 50 OFFSET 1000000;
Кажется, что база должна просто пропустить миллион строк и вернуть следующие 50. Но SQL-движок не может сделать настоящий прыжок через
OFFSET. Ему нужно обработать строки до нужной позиции, а потом отбросить их.Без подходящего индекса это может выглядеть так: база читает строки, определяет порядок сортировки, обрабатывает
OFFSET + LIMIT записей, выбрасывает ненужные строки и возвращает только нужный результат.Даже если есть индекс:
CREATE INDEX idx_events_created_at
ON events(created_at DESC);
ситуация всё равно не идеальна.
Индекс помогает быстрее читать данные в нужном порядке, но базе всё равно приходится пройти большое количество записей, чтобы добраться до нужного
OFFSET.Например:
LIMIT 50 OFFSET 1000000;
означает, что базе нужно обработать примерно миллион строк перед тем, как вернуть результат.
Для глубоких страниц обычно используют keyset pagination (cursor pagination). Вместо номера страницы передаётся последнее значение из предыдущего результата:
SELECT
id,
user_id,
created_at
FROM events
WHERE created_at < '2026-08-01 12:00:00'
ORDER BY created_at DESC
LIMIT 50;
Теперь база может использовать индекс и сразу искать позицию, с которой нужно продолжить чтение.
Но одного
created_at недостаточно, если несколько событий имеют одинаковое время.Например:
2026-08-01 12:00:00
2026-08-01 12:00:00
2026-08-01 12:00:00
Порядок становится неоднозначным. Поэтому добавляют уникальный ключ:
CREATE INDEX idx_events_cursor
ON events(created_at DESC, id DESC);
И запрос становится:
SELECT
id,
user_id,
created_at
FROM events
WHERE (created_at, id) < ('2026-08-01 12:00:00', 1500000)
ORDER BY created_at DESC, id DESC
LIMIT 50;
Теперь курсор точно определяет позицию в индексе.
Главное преимущество такого подхода — стоимость запроса зависит в основном от размера страницы, а не от глубины пагинации. Первая страница:
LIMIT 50;
и страница после миллионов записей:
WHERE (created_at, id) < (...)
LIMIT 50;
используют один и тот же принцип: найти позицию в B-tree индексе и продолжить чтение.
OFFSET остаётся нормальным решением для административных интерфейсов, внутренних инструментов и небольших таблиц. Но для лент событий, истории операций, логов и больших списков он быстро становится узким местом.OFFSET удобен для разработки, но плохо масштабируется на больших объёмах данных. Для больших таблиц лучше использовать keyset pagination с составным индексом и стабильным курсором.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍7🤝7
В этой статье:
• Разберётесь, почему для денег и точных расчётов часто выбирают numeric, несмотря на его производительность;• Узнаете, чем на практике отличаются numeric, целые типы и double precision;• Посмотрите, какие требования к точности и округлению предъявляют SQL, платёжные системы и финансовые стандарты.🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12🔥7👍5
Настройки только для одного запроса!
Необязательно менять
Так миграция не будет бесконечно ждать блокировку: если таблица занята дольше заданного времени, PostgreSQL завершит операцию ошибкой.
Можно даже точечно дать тяжёлому запросу больше памяти, не увеличивая
🔥
➡️ SQL Ready | #совет
Необязательно менять
statement_timeout, work_mem или другие параметры для всей сессии. SET LOCAL действует только до конца текущей транзакции.BEGIN;
SET LOCAL lock_timeout = '500ms';
ALTER TABLE orders
ADD COLUMN source text;
COMMIT;
Так миграция не будет бесконечно ждать блокировку: если таблица занята дольше заданного времени, PostgreSQL завершит операцию ошибкой.
BEGIN;
SET LOCAL work_mem = '512MB';
SELECT customer_id, count(*)
FROM events
GROUP BY customer_id;
COMMIT;
Можно даже точечно дать тяжёлому запросу больше памяти, не увеличивая
work_mem для остальных запросов соединения.SET LOCAL позволяет тюнить PostgreSQL под конкретную операцию и автоматически возвращает настройки назад после COMMIT или ROLLBACK.Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍7🔥5🤝1
This media is not supported in your browser
VIEW IN TELEGRAM
Здесь собраны разборы типичных ситуаций при работе с PostgreSQL: проблемы с подключением и настройкой, ошибки в SQL, оптимизация медленных запросов, партиционирование, размеры таблиц,
timestamp with time zone, pg_dump и другие нюансы. Есть конкретные команды, примеры диагностики и рекомендации по тому, какие данные собирать для поиска проблем. Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤8👍6🤝2
Шпаргалка по работе с последовательностями в PostgreSQL: создание и удаление sequence, получение новых и текущих значений, сброс и синхронизация счётчика после импорта, а также привязка последовательности к столбцу. Помогает управлять генерацией идентификаторов и корректно работать со счётчиками при конкурентных вставках и загрузке данных.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤7🤝3🔥2