SQL Ready | Базы Данных
Авторский канал про Базы Данных и SQL Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Cотрудничество: @energy_c РКН: https://clck.ru/3QREBc
Mostrar más📈 Análisis del canal de Telegram SQL Ready | Базы Данных
El canal SQL Ready | Базы Данных (@sql_ready) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 17 660 suscriptores, ocupando la posición 7 358 en la categoría Tecnologías y Aplicaciones y el puesto 37 256 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 17 660 suscriptores.
Según los últimos datos del 16 octubre, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de 653, y en las últimas 24 horas de 130, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 12.22%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 6.06% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 2 155 visualizaciones. En el primer día suele acumular 1 069 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 21.
- Intereses temáticos: El contenido se centra en temas clave como sql, строка, users, индекс, user_id.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“Авторский канал про Базы Данных и SQL
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!
Cотрудничество: @energy_c
РКН: https://clck.ru/3QREBc”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 17 octubre, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
Шпаргалка по агрегации данных и формированию отчётности в Oracle SQL: объединение значений с LISTAGG, выбор значений по рангу с KEEP (DENSE_RANK FIRST/LAST), построение промежуточных и общих итогов с ROLLUP и GROUPING SETS, а также идентификация уровней агрегации с помощью GROUPING и GROUPING_ID. Полезно для построения сводных выборок, многоуровневых отчётов и обработки агрегированных данных непосредственно средствами SQL.
➡️ SQL Ready | #шпораCREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
status VARCHAR(30),
created_at TIMESTAMP NOT NULL,
amount NUMERIC(10,2)
);
Обычный индекс на каждую колонку не всегда является оптимальным решением. СУБД может использовать несколько индексов одновременно, но такой план не всегда будет эффективнее одного правильно спроектированного составного индекса.
CREATE INDEX idx_orders_user
ON orders(user_id);
CREATE INDEX idx_orders_created_at
ON orders(created_at);
Для частого сценария поиска заказов конкретного пользователя за период лучше создать составной индекс:
CREATE INDEX idx_orders_user_created_at
ON orders(user_id, created_at);
Такой индекс эффективно работает для запросов, где используется первая колонка индекса или полный набор колонок.
SELECT *
FROM orders
WHERE user_id = 42
AND created_at >= '2026-01-01';
В B-tree индексе данные сначала сортируются по user_id, а внутри одинаковых значений user_id — по created_at.
Поэтому СУБД быстро находит записи пользователя и затем выполняет поиск по диапазону дат. Также индекс будет использоваться:
SELECT *
FROM orders
WHERE user_id = 42;
Но он плохо подходит для поиска только по второй колонке:
SELECT *
FROM orders
WHERE created_at >= '2026-01-01';
Причина в структуре B-tree: данные сначала организованы по первой колонке индекса.
Для такого запроса отдельный индекс по дате будет более подходящим:
CREATE INDEX idx_orders_created_at
ON orders(created_at);
Ещё одна распространённая ошибка — добавление большого количества колонок в индекс.
CREATE INDEX idx_orders_all_columns
ON orders(user_id, status, created_at, amount);
Каждый дополнительный столбец увеличивает размер индекса и стоимость операций записи.
Перед созданием индекса нужно анализировать реальные запросы приложения, а не добавлять поля на всякий случай.
EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE user_id = 42
AND status = 'paid'
ORDER BY created_at DESC;
Для такого запроса может быть эффективнее индекс, учитывающий фильтрацию и сортировку:
CREATE INDEX idx_orders_user_status_created_at
ON orders(user_id, status, created_at DESC);
Правильно спроектированный составной индекс уменьшает количество операций чтения, снижает нагрузку на CPU и помогает оптимизатору выбрать более дешёвый план выполнения.
🔥 Главное правило простое: индекс создаётся под реальные запросы приложения, а не просто под структуру таблицы.
➡️ SQL Ready | #практикаROWS считает физические строки. GROUPS считает группы строк с одинаковым значением ORDER BY.
SELECT price,
count(*) OVER (
ORDER BY price
GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING
)
FROM products;
Если по одной цене находится 50 товаров, вся эта пачка считается одной группой. Поэтому можно строить окна относительно соседних значений, не вычисляя dense_rank() и не добавляя ещё один уровень запроса.
Ещё интереснее EXCLUDE, который можно использовать прямо внутри оконного фрейма:
SELECT id,
avg(score) OVER (
ORDER BY created_at
ROWS BETWEEN 10 PRECEDING AND 10 FOLLOWING
EXCLUDE CURRENT ROW
) AS neighbors_avg
FROM measurements;
Среднее здесь считается по соседним строкам, но текущая строка автоматически исключается.
SELECT price,
sum(amount) OVER (
ORDER BY price
GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
EXCLUDE GROUP
)
FROM sales;
EXCLUDE GROUP исключит из расчёта всю текущую peer-группу, а не только одну строку.
🔥 GROUPS + EXCLUDE позволяют описывать сложные окна непосредственно в OVER, где обычно появляются dense_rank(), дополнительные CTE и self join.
➡️ SQL Ready | #советОставляю ссылочку: GitHub 📱➡️ SQL Ready | #репозиторий
cast падает с ошибкой, если значение нельзя преобразовать. Из-за этого в ETL и в запросах к промежуточным данным часто приходится заранее чистить данные или писать PL/pgSQL с обработкой исключений.
SELECT value::integer
FROM staging;
-- ERROR при первом плохом значении
PostgreSQL умеет проверять, является ли строка допустимым значением конкретного типа, не вызывая ошибку.
SELECT value
FROM staging
WHERE pg_input_is_valid(value, 'integer');
Типом может быть не только integer:
SELECT pg_input_is_valid('192.168.1.10', 'inet');
SELECT pg_input_is_valid('550e8400-e29b-41d4-a716-446655440000', 'uuid');
SELECT pg_input_is_valid('2026-09-08', 'date');
А pg_input_error_info() позволяет получить причину, почему преобразование невозможно.
SELECT *
FROM pg_input_error_info('2026-02-30', 'date');
🔥 pg_input_is_valid() позволяет валидировать грязные входные данные средствами самой системы типов PostgreSQL.
➡️ SQL Ready | #советШпаргалка по работе с последовательностями в PostgreSQL: создание и удаление sequence, получение новых и текущих значений, сброс и синхронизация счётчика после импорта, а также привязка последовательности к столбцу. Помогает управлять генерацией идентификаторов и корректно работать со счётчиками при конкурентных вставках и загрузке данных.
➡️ SQL Ready | #шпораtimestamp with time zone, pg_dump и другие нюансы. Есть конкретные команды, примеры диагностики и рекомендации по тому, какие данные собирать для поиска проблем.
Оставляю ссылочку: GitHub 📱➡️ 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.
➡️ SQL Ready | #совет• Разберётесь, почему для денег и точных расчётов часто выбирают numeric, несмотря на его производительность;
• Узнаете, чем на практике отличаются numeric, целые типы и double precision;
• Посмотрите, какие требования к точности и округлению предъявляют SQL, платёжные системы и финансовые стандарты.
🔊 Продолжай читать на Habr!➡️ 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 с составным индексом и стабильным курсором.
➡️ SQL Ready | #практикаCache позволяет отдавать часто используемые данные без обращения к базе данных, а Load Balancer распределяет запросы между несколькими серверами, повышая скорость и отказоустойчивость приложения.
На картинке — основные стратегии уменьшения задержек в высоконагруженных системах.
Сохрани, чтобы не потерять!
➡️ SQL Ready | #ресурс