7786
Авторский канал про Базы Данных и SQL Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Автор: @energy_it
🖥 GROUPING SETS — несколько отчётов в одном запросе!
В аналитике часто нужно считать данные сразу на нескольких уровнях: детализация, промежуточные итоги и общий результат. GROUPING SETS позволяет описать эту структуру напрямую.
Сегодня в гайде:
• Как считать несколько уровней агрегации за один проход по данным;
• Как отличать строки-итоги от обычных данных;
• Почему такой подход проще поддерживать и масштабировать.
DISTINCT vs GROUP BY — выбираем правильный инструмент для удаления дублей!
В SQL часто нужно избавиться от повторяющихся строк: уникальные пользователи, товары, категории. Для этого используют DISTINCT и GROUP BY. Результат может выглядеть одинаково, но назначение и смысл у этих конструкций разные.
Представим таблицу заказов:
orders(id, customer_id, product_id)
SELECT DISTINCT customer_id
FROM orders;
DISTINCT удаляет дубликаты по всему набору выбранных колонок в результирующем наборе — без группировок и агрегаций.GROUP BY:SELECT customer_id
FROM orders
GROUP BY customer_id;
customer_id. В простых случаях оптимизатор часто строит одинаковый план, но логика запроса уже «про группы».GROUP BY становится необходимым, когда появляются агрегаты.SELECT customer_id, COUNT(*) AS orders_count
FROM orders
GROUP BY customer_id;
GROUP BY обязателен, потому что мы одновременно выбираем агрегат (COUNT(*)) и неагрегированное поле (customer_id).DISTINCT и агрегаты без GROUP BY:SELECT DISTINCT customer_id, COUNT(*)
FROM orders;
GROUP BY. В зависимости от СУБД и режима он либо не выполнится, либо вернёт неопределённый результат.SELECT customer_id, COUNT(*) AS orders_count
FROM orders
GROUP BY customer_id;
DISTINCT для простого удаления дублей, а GROUP BY — когда нужна агрегация, расчёты по группам или HAVING.
Как избежать блокировок таблиц с помощью advisory locks в PostgreSQL!
Иногда нужно гарантировать, что только один процесс выполняет критическую секцию, но при этом не хочется блокировать таблицы и строки.
Для этого PostgreSQL предоставляет advisory locks — логические блокировки, не привязанные к таблицам или строкам.
SELECT pg_advisory_xact_lock(42);
user_id, order_id, хеш или tenant_id.SELECT pg_advisory_xact_lock(user_id);
COMMIT или ROLLBACK блокировка снимается автоматически.
❤️ SQL-101 — руководство, помогающее освоить язык и укрепить базу тем, кто уже работает с БД.
В этом ресурсе собраны ключевые темы, которые нужны в работе: базовые запросы, фильтрация, JOIN, группировки, подзапросы, индексы, транзакции и основы оптимизации. Всё объяснено простым языком и дополнено примерами с упражнениями.
Оставляю ссылочку: Github 📱
⚡️ ВАЙБ-КОДИНГ теперь в Telegram!
Ребята сделали крутейший канал, где на наглядных примерах и понятном языке рассказывают как войти в новую эру разработки с ИИ, делятся полезными фишками и инструментами
Подписывайтесь, нас уже 10 тысяч: @vibecoding_tg
Как понять, какие индексы только тратят место?
Ненужные индексы замедляют вставки, обновления и VACUUM. PostgreSQL умеет показать, какие индексы ни разу не использовались.
Посмотрим статистику использования:
SELECT relname, idx_scan, pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_stat_user_indexes
WHERE idx_scan = 0;
idx_scan = 0 — индекс ни разу не участвовал в плане.size покажет, сколько места он занимает на диске.SELECT relname, idx_scan, idx_tup_read
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC
LIMIT 10;
Keyset-пагинация: быстрый скролл без OFFSET!OFFSET…LIMIT прост, но плохо масштабируется: чем дальше страница, тем медленнее запрос и выше риск дубликатов при вставках.Keyset использует курсор (id/дату) и даёт стабильную скорость на больших объёмах.
Создаём таблицу (пример на PostgreSQL):
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CTE:WITH cursor AS (
SELECT 1000::BIGINT AS last_seen_id
)
OFFSET по keyset-подходу:SELECT p.id, p.title, p.created_at
FROM posts p
JOIN cursor c ON TRUE
WHERE p.id < c.last_seen_id
ORDER BY p.id DESC
LIMIT 20;
id < last_seen_id.last_seen_id для следующего запроса.
✍️ LangShift — учись новому языку программирования, используя знания, которые у тебя уже есть!
Этот сайт предлагает другой путь: выбираешь язык, который уже знаешь, и переходишь на новый через сопоставление синтаксиса и парадигм. Более 80 модулей, 30+ проектов, всё бесплатно и без регистрации.
📌 Оставляю ссылочку: langshift.dev
➡️ SQL Ready | #ресурс
😎 На Хабре вышла полезная статья: «6 лайфхаков при внедрении СУБД: учимся на чужих граблях»!
В этой статье:• Разберёте реальные ошибки при развёртывании СУБД;
• Узнаете, как повысить производительность запросов через правильное партицирование и не только;
• Поймёте, как организовать конкурентный доступ и обновления данных без блокировок и простоев;
• Получите шесть конкретных лайфхаков, которые помогут избежать критических проблем.
🔊 Продолжайте читать на Habr!
😎 Sqltest - бесплатный онлайн-тренажёр для практики запросов прямо в браузере!
Вам будут доступы более 320 интерактивных задач разной сложности: от простых SELECT-запросов до вложенных подзапросов и агрегаций. Поддерживаются MySQL, PostgreSQL, MS SQL и Firebird, есть мгновенная проверка решений и удобный интерфейс для отработки навыков на практике.
📌 Оставляю ссылочку: sqltest.online
➡️ SQL Ready | #ресурс
📂 Напоминалка по оптимизации производительности БД!
Например, грамотное индексирование ускоряет выборки в разы, шардирование помогает масштабировать систему под высокий трафик, а репликация повышает отказоустойчивость и снижает нагрузку на основной узел.
На изображении — структурированное напоминание о ключевых метриках, типах нагрузок и практических стратегиях оптимизации.
Сохрани, чтобы не забыть!
➡️ SQL Ready | #ресурс
🖥 Какие API-маршруты действительно тормозят систему?
В производительных системах реальный UX определяется не средним временем ответа, а редкими хвостовыми задержками, из-за которых важны именно p95 и p99.
Сегодня в задаче:
• Посчитаем ключевые перцентильные метрики (p50/p95/p99) по каждому маршруту;
• Определим, какие запросы регулярно превышают собственный p99;
• Увидим, какие эндпоинты “убивают” отклик сервиса под нагрузкой;
API и контролировать качество работы системы.
☕️ CodeAbbey — тренажёр алгоритмического мышления!
Сайт с огромной коллекцией задач по программированию, от самых простых до тех, что реально заставят подумать.
Учиться можно на абсолютно любом языке. Отличный способ подтянуть логику, научиться писать аккуратный код и подготовиться к собесам.
📌 Оставляю ссылочку: codeabbey.com
➡️ SQL Ready | #ресурс
QUALIFY: фильтрация после оконных функций!
Иногда нужно фильтровать строки после вычисления оконных функций. В большинстве диалектов SQL для этого нужен подзапрос, но в ряде СУБД (Snowflake, BigQuery, Teradata, Oracle 23c) есть конструкция QUALIFY, позволяющая делать это напрямую.
Отбираем строки только с первым местом внутри категории:
SELECT id, category, score,
RANK() OVER (
PARTITION BY category
ORDER BY score DESC
) AS rnk
FROM results
QUALIFY rnk = 1;
SELECT id, value,
value - LAG(value) OVER (
ORDER BY id
) AS diff_prev
FROM metrics
QUALIFY diff_prev > 50;
SELECT customer_id, order_id, amount,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY amount DESC
) AS rn
FROM orders
QUALIFY rn <= 3;
QUALIFY помогает писать чище и короче, избавляя от лишних подзапросов. Особенно полезен в аналитических задачах с большим числом оконных функций.
Как увидеть фрагментацию таблиц?
Часто производительность падает из-за накопления устаревших (dead) кортежей, которые остаются после UPDATE/DELETE до следующего VACUUM.
Посмотреть их реальный объём можно напрямую из системной статистики:
SELECT relname,
n_dead_tup,
n_live_tup
FROM pg_stat_all_tables
WHERE relname = 'orders';
n_dead_tup — количество устаревших кортежей, n_live_tup — актуальные строки.SELECT relname, n_dead_tup
FROM pg_stat_all_tables
ORDER BY n_dead_tup DESC
LIMIT 10;
SELECT relname,
n_dead_tup,
n_live_tup,
round(100.0 * n_dead_tup / (n_live_tup + 1), 2) AS dead_ratio
FROM pg_stat_all_tables;
Как корректно сравнивать значения с NULL?
Обычное сравнение может сломаться, когда в данных появляется NULL. В SQL выражение:
email <> 'admin@example.com'
email IS DISTINCT FROM 'admin@example.com'
old_value IS DISTINCT FROM new_value
📂 Напоминалка по структурам данных для экономии памяти и работы с большими данными!
Например, Bloom Filter позволяет быстро проверить, встречался ли элемент ранее, а HyperLogLog помогает оценить количество уникальных значений, не храня все данные целиком.
На картинке — 6 структур данных, которые стоит держать под рукой при проектировании backend-систем, аналитики и highload-сервисов.
Сохрани, чтобы не забыть!
➡️ SQL Ready | #ресурс
Префиксные индексы в MySQL — ускоряем поиск по длинным строкам!
Полные индексы на длинных строках занимают много ресурсов, тогда как префиксные индексируют только первые N символов, уменьшая объём индекса и ускоряя поиск при высокой селективности начала строки.
Создадим таблицу с длинным текстовым атрибутом — типичный кейс, где полный индекс был бы слишком тяжёлым:
CREATE TABLE documents (
id INT PRIMARY KEY,
doc_key VARCHAR(500) NOT NULL
);
CREATE INDEX idx_doc_key_prefix
ON documents (doc_key(20));
SELECT id
FROM documents
WHERE doc_key LIKE 'INV-2024-%';
CREATE INDEX idx_email_prefix
ON users (email(16));
Почему Index Only Scan в PostgreSQL не всегда работает?
Если PostgreSQL не использует Index Only Scan, проблема часто не в запросе и не в самом индексе.Index Only Scan работает только если страницы помечены как видимые в visibility map. Если этого нет PostgreSQL всё равно идёт в таблицу.
Проверьте план выполнения:
EXPLAIN ANALYZE
SELECT id FROM orders WHERE status = 'pending';
Index Scan, а не Index Only Scan, одна из частых причин в том, что visibility map не заполнена (при наличии подходящего covering index).VACUUM (ANALYZE) orders;
VACUUM помечает страницы как all-visible, и PostgreSQL может перестать читать таблицу.
📂 Напоминалка для работы с индексами в базах данных!
Например, B+ Tree Index используется для быстрого поиска и сортировки, а Hash Index подходит для точных совпадений по ключу.
На картинке — 5 основных структур данных, на которых строятся индексы в современных СУБД.
Сохрани, чтобы не забыть!
➡️ SQL Ready | #ресурс
Забудь про ChatGPT. Это как просить калькулятор нарисовать картину.
Пока массы гоняют одни и те же скучные запросы в зацензуренные боты, реальная революция ИИ в канале «Техноразум»:
— Нейросети, которые не боятся запретных тем ( 🔞)
— Скрытые функции, которые другие ИИ прячут за платную подписку или цензурой
— Настоящие инструкции и промты для взлома творческих шаблонов
Переходи к настоящим возможностям, забудь про детский сад нейросетей: /channel/+7dOPAyODQ6
Почему ваше резюме попадает в папку «Отказы»?
На картинке вы видите 295 отказов.
Среднестатистический соискатель думает: «Нужно лучше составить резюме» или «У меня мало опыта».
Рекрутер видит другое: 295 ошибок.
❌ Неправильно выбранная стратегия откликов.
❌ Ключевые слова, которые не видит система ATS.
❌ Отклик на ФЕЙКОВЫЕ вакансии.
Работодатели не будут учить вас, как проходить их собственные фильтры. Это не в их интересах. Но это в наших интересах.
Мы — @Mathcareer — знаем систему изнутри.
Хотите узнать, что на самом деле скрывается за отказами и как это исправить? Подписывайтесь: /channel/+erDge2ZLK4BmM2Ri
🖥 Как восстановить пользовательские сессии без session_id?
SQL позволяет восстановить сессии даже без session_id, выделяя их по временным разрывам и последовательности событий.
Сегодня в задаче:
• Определим моменты, когда начинается новая сессия;
• Присвоим каждому событию уникальный session_id с помощью оконной суммы;
• Получим полноценные сессии так же, как это делают продуктовые аналитические платформы.
Работа со строками в PostgreSQL — извлекаем данные с помощью регулярных выражений!
В аналитике часто нужно разобрать строку: вытащить домен из email, код из SKU, номер из текста. PostgreSQL предоставляет функции regexp_match и regexp_replace, позволяющие делать это напрямую в SQL.
Создадим таблицу:
CREATE TABLE users (
id INT,
email TEXT,
profile_code TEXT
);
SELECT
id,
email,
(regexp_match(email, '@(.+)$'))[1] AS domain
FROM users;
regexp_match возвращает один массив, и [1] достаёт первую группу. Паттерн @(.+)$ берёт всё, что стоит после символа @.USR-2391-A":SELECT
id,
profile_code,
(regexp_match(profile_code, '([0-9]+)'))[1] AS numeric_part
FROM users;
([0-9]+) извлекает последовательность цифр.SELECT
id,
regexp_replace(profile_code, '[^A-Za-z0-9]', '', 'g') AS cleaned
FROM users;
Как точечно понять, почему конкретный индекс НЕ используется оптимизатором?
Если запрос игнорирует индекс, причина может быть не в индексе, а в том, что PostgreSQL не знает, насколько селективное значение в колонке.
Обновить статистику можно точечно, для одной конкретной колонки:
ANALYZE users (status);
Index Scan.ALTER TABLE users
ALTER COLUMN status SET STATISTICS 500;
ALL и ANY в SQL — учимся использовать для сравнения с подзапросами!
Эти операторы предназначены для сравнения результатов одного SELECT с результатами второго SELECT из подзапроса, что может быть удобно в некоторых случаях: если подзапрос возвращает небольшое количество строк или когда нужно сравнить значение хотя бы с одним значением из подзапроса.
Представим, что нам нужно найти все продукты, цена которых выше, чем цена любого продукта в категории Discount:
SELECT product, price
FROM products
WHERE price > ALL (SELECT price FROM products WHERE category = 'Discount');
SELECT DISTINCT customer_id
FROM orders
WHERE product_id = ANY (SELECT product_id FROM products WHERE price > 1000);
SELECT DISTINCT customer_id
FROM orders
WHERE product_id = ANY (SELECT product_id FROM products WHERE category = 'Electronics');
ALL и ANY возможно только с подзапросами и может быть неэффективным, если подзапрос возвращает большое количество строк.
RETURNING: получение данных прямо из DML!
Во многих СУБД (например, PostgreSQL, Oracle и др.) оператор RETURNING позволяет вернуть значения вставленных, обновлённых или удалённых строк без дополнительного SELECT.
Вставляем строку и сразу получаем созданный ID:
INSERT INTO users (name, email)
VALUES ('Anna Ivanova', 'anna@example.com')
RETURNING id;
UPDATE accounts
SET balance = balance + 500
WHERE id = 10
RETURNING balance AS new_balance;
DELETE FROM orders
WHERE id = 42
RETURNING order_date, amount;
UPDATE products
SET price = price * 1.10
WHERE category = 'Books'
RETURNING id, price;
RETURNING упрощает логику: нет необходимости выполнять дополнительный запрос, чтобы получить новые данные — они доступны сразу в рамках одного DML.
🖥 Методы для работы с датой и временем в PostgreSQL!Эта шпаргалка охватывает наиболее используемые функции PostgreSQL для получения текущего времени, извлечения компонентов дат, расчета интервалов, округления временных меток и преобразования Unix-времени. Подходит для разработки систем, где критична точная и предсказуемая работа с временными значениями.
➡️ SQL Ready | #шпора
🖥 Selectivity — почему индекс может не сработать?
Селективность определяет, сколько строк проходит через фильтр, и именно по этому показателю оптимизатор выбирает план запроса.
Сегодня в гайде:
• Как селективность влияет на выбор плана;
• Почему один и тот же запрос может работать по-разному на разных данных;
• Как устаревшая статистика приводит к “не тем” решениям оптимизатора.
🖥 Как восстановить состояние LRU-кэша после серии обращений?
Возьмём последовательность обращений к ключам и с помощью SQL определим, какие из них “выжили” в памяти при ограниченном размере кэша.
Сегодня в задаче:
• Найдём последнее обращение к каждому ключу, определив его актуальность на момент завершения всех операций.
• Отсортируем элементы по “новизне” использования и применим ограничение capacity;
• Получим итоговое состояние LRU-кэша;