Базы данных (Data Base) – Telegram
Базы данных (Data Base)
8.19K subscribers
576 photos
468 videos
19 files
555 links
Базы данных (Data Base). По всем вопросам @evgenycarter
Download Telegram
🚨 Как понять, почему запрос тормозит?

Сегодня покажу простой, но действенный подход к диагностике медленных SQL-запросов. Когда к тебе приходит прод с жалобой "что-то всё виснет", важно не паниковать, а системно подойти к анализу.

Вот что я делаю первым делом:

1. Включаю EXPLAIN (ANALYZE)
Это ваш лучший друг. Не EXPLAIN, а именно ANALYZE, чтобы получить реальные значения времени, а не план на бумаге.

2. Смотрю на узлы с наибольшим временем
Часто виновник — Seq Scan по большой таблице или Nested Loop с миллионами итераций.

3. Ищу несработавшие индексы
Был ли Index Scan или Index Only Scan? Если нет — стоит проверить, почему не сработал индекс. Может, фильтр не селективный?

4. Проверяю фильтрацию и сортировку
ORDER BY может убить всё. Особенно если не по индексу.

5. Думаю про статистику
ANALYZE делали недавно? PostgreSQL может строить плохой план, если у него устаревшие данные.


🛠 Если ты часто отлаживаешь SQL — советую поставить pgMustard или использовать EXPLAIN DEPOT. Они визуализируют планы и сразу показывают узкие места.

📲 Мы в MAX

#db

👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
📕Базовые принципы шардирования и репликации в ClickHouse
👤Вебинар для: Data-инженеров, архитекторов и аналитиков, администраторам баз данных и DevOps-инженеров, разработчиков высоконагруженных приложений

На открытом уроке 18 декабря в 20:00 мск мы разберемся в двух ключевых механизмоах для работы с большими данными в ClickHouse.

📗 На вебинаре разберем:
1. Что такое шардирование и репликация и зачем они нужны.
2. Как эти механизмы устроены внутри ClickHouse.

📘 В результате на практике изучите основные понятия (шард, реплика, Distributed-таблицы), поймете, как распределяются данные и обеспечивается отказоустойчивость, и узнаете, с чего начать проектирование кластера ClickHouse.

👉 Регистрация на урок и подробности о курсе ClickHouse для инженеров и архитекторов БД: https://vk.cc/cSoBSx

Все участники открытого урока получат скидку на курс "ClickHouse для инженеров и архитекторов БД"

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
👍1
🚀 Сегодня я покажу вам один из моих любимых хаков для PostgreSQL – генерация серий дат без циклов и хранимок. Это идеальный способ быстро собрать таймлайн для аналитики или отчётов.

Сценарий: вам нужно построить список всех дат за последний месяц — например, чтобы потом сделать LEFT JOIN к таблице с событиями и увидеть, где были пропуски.

Вот как это делается с помощью generate_series:


SELECT generate_series(
date_trunc('day', current_date) - interval '30 days',
date_trunc('day', current_date),
interval '1 day'
) AS day;


💡 Результат — 31 строка с датами от 30 дней назад до сегодняшнего дня.

Теперь добавим, например, LEFT JOIN к таблице events, чтобы увидеть активность по дням:


SELECT
d.day,
COUNT(e.id) AS events_count
FROM
generate_series(
date_trunc('day', current_date) - interval '30 days',
date_trunc('day', current_date),
interval '1 day'
) AS d(day)
LEFT JOIN events e ON date_trunc('day', e.created_at) = d.day
GROUP BY d.day
ORDER BY d.day;


📊 Отлично подходит для дашбордов, когда нужно увидеть, где были дни без событий.

Пользуетесь ли вы generate_series? А может быть, используете что-то подобное в других СУБД? Делитесь в комментариях👇

📲 Мы в MAX

#db

👉 @database_info
👍8
Сегодня я хочу рассказать вам про одну часто недооцененную фишку в PostgreSQL - partial indexes (частичные индексы).

Обычно мы создаём индексы на всю таблицу, но что если нам нужно ускорить только небольшую часть данных? Например, часто выбираются только активные пользователи (status = 'active'). Вместо полного индекса можно создать индекс только для нужного поднабора данных:


CREATE INDEX idx_active_users
ON users (last_login)
WHERE status = 'active';


Что это даёт:
- Индекс меньше по размеру → быстрее поиск и обновление.
- Используется только тогда, когда запрос соответствует условию status = 'active'.
- Меньше нагрузка на диск при обновлениях таблицы.

🛠 Где это реально помогает:
- Таблицы с миллионами записей, где активно работают только с частью строк.
- Сценарии "горячих" и "холодных" данных.

Рекомендую попробовать partial indexes там, где обычные индексы слишком тяжелы или тормозят обновления!

📲 Мы в MAX

#db

👉 @database_info
👍72
⚡️Платите меньше за хранение логов и бэкапов

В S3 Selectel появился новый класс хранилища — ледяное. Оно оптимизировано под большие объемы редко используемых данных с типом репликации — Erasure Coding.

Теперь можно выбирать хранилище точно под задачу и экономить до 30% на хранении данных:
📊 для востребованных данных — стандартное хранилище,
🗄 для архивов — холодное,
📦 для логов, бэкапов и документов — ледяное, от 0,79 ₽/мес за 1 ГБ данных.

Переносите данные в S3 Selectel по акции «миграционные каникулы»: первый месяц хранение и входящие запросы будут бесплатными.
Создавайте заявку и переносите данные без лишних расходов → https://slc.tl/q66pd

Реклама. АО "Селектел". erid:2W5zFGewzkY
🔎 Мини-гайд: Индексы в PostgreSQL — быстро и по делу

Индексы — главный инструмент для ускорения запросов. Но неправильное использование может только навредить.

Основные типы индексов в PostgreSQL:
- B-tree — по умолчанию. Идеален для поиска по равенству и диапазону (=, <, >, BETWEEN).
- Hash — только для поиска по точному равенству (=). Становится актуальным реже.
- GIN — для массивов, JSONB, полнотекстового поиска.
- GiST — геоданные, поиск по диапазонам, сложные типы.
- BRIN — для очень больших таблиц с упорядоченными данными (например, логи).

Практические советы:
- Не злоупотребляй индексами: каждый индекс замедляет INSERT/UPDATE/DELETE.
- Следи за актуальностью: периодически проверяй и удаляй неиспользуемые (pg_stat_user_indexes поможет).
- Составные индексы ((col1, col2)) эффективны, только если условия WHERE учитывают порядок колонок.
- Используй EXPLAIN ANALYZE, чтобы понять, работает ли индекс в реальности.

Типичная ошибка:
Создать индекс на всё подряд без анализа запросов. Итог — тормоза на записи и огромный размер базы.

Индексы — это как специи: мало — пресно, много — несъедобно.


Вывод:
Хотите быструю базу — планируйте индексацию так же внимательно, как сами запросы.

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

📲 Мы в MAX

#db

👉 @database_info
👍81
Антипаттерн: Хранить даты и время в VARCHAR

Встречали такое?


CREATE TABLE orders (
id SERIAL PRIMARY KEY,
order_date VARCHAR(20)
);


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

🔴 Нет гарантии формата
'2024-12-01', '12/01/2024', '01.12.24', 'вчера' — всё ляжет, но работать с этим потом боль.

🔴 Сложность фильтрации и сортировки
Сравнение строк ≠ сравнение дат.
Запросы типа WHERE order_date > '2024-01-01' могут вести себя непредсказуемо.

🔴 Нельзя использовать функции времени
Ни DATE_TRUNC, ни AGE(), ни агрегаты по времени не работают нормально с VARCHAR.

Как правильно
Используйте типы DATE, TIMESTAMP, TIMESTAMPTZ — они:

* валидируют данные на вставке;
* дают мощный инструментарий для анализа;
* упрощают работу с часовыми поясами и интервалами.


CREATE TABLE orders (
id SERIAL PRIMARY KEY,
order_date TIMESTAMPTZ DEFAULT now()
);


💡 Если данные приходят в виде строк — парси их при загрузке, а не храни как есть.


Сохрани, чтобы не наступить на эти же грабли ☝️
А как у вас хранят даты?

📲 Мы в MAX

#db

👉 @database_info
👍9👎1
Антипаттерн: «Одна таблица на всё»

Когда бизнес-логика усложняется, а структура БД остаётся в духе Excel — жди беды.

🔴 Что это такое?
Проектировщик (часто на раннем этапе) создаёт одну большую таблицу, где:
– сотни колонок на все случаи жизни,
– куча NULL-ов,
– смешаны данные разных сущностей (например, и клиенты, и заказы, и статусы).

Так проще… пока не начнётся работа с реальными данными.

💥 Что пойдёт не так:
– Производительность падает: индексы не работают эффективно.
– Сложность валидации и бизнес-логики.
– Трудно расширять: каждое изменение — как операция на открытом сердце.
– Нельзя нормально нормализовать: всё связано со всем.

Как избежать:
– Используй нормализацию: выноси повторяющиеся и логически независимые данные в отдельные таблицы.
– Не бойся JOIN-ов — это не зло, а инструмент.
– Планируй схему БД под задачи, а не наоборот.

📌 Пример:
Плохо:


CREATE TABLE everything (
id INT,
client_name TEXT,
order_price DECIMAL,
order_status TEXT,
delivery_address TEXT,
...
);


Хорошо:


CREATE TABLE clients (
id INT PRIMARY KEY,
name TEXT
);

CREATE TABLE orders (
id INT PRIMARY KEY,
client_id INT REFERENCES clients(id),
price DECIMAL,
status TEXT
);


📎 Вывод: одна таблица ≠ проще. Это короткий путь к хаосу.
Разделяй и властвуй.

Сохрани, чтобы не пришлось рефакторить через полгода 👇

📲 Мы в MAX

#db

👉 @database_info
👍82
🎯 Мини-гайд: как НЕ спроектировать монстра вместо схемы БД

Когда проект только начинается, есть соблазн «не заморачиваться» и сделать одну большую таблицу на всё.
Спойлер: потом будет больно.

Вот как этого избежать 👇

1. Начинай с нормализации
– Смотри, какие поля повторяются.
– Разделяй по сущностям: клиент ≠ заказ ≠ товар.
– Нормальные формы — не академикам, а тебе на пользу.

2. Определи связи заранее
– Один ко многим? Многие ко многим?
– Используй FOREIGN KEY, чтобы база помогала тебе, а не мешала.

3. Не бойся JOIN-ов
– Да, их становится больше.
– Но это лучше, чем сотни NULL - ов и "status_1", "status_2" в одной колонке.

4. Планируй под рост
– Временные костыли становятся постоянными.
– Заложи масштабируемость: разнос сущностей, отдельные таблицы для логов, истории, связей.

5. Назначь ID-шки как бог велел
PRIMARY KEY + автоинкремент или UUID.
– Не делай email или name ключом — это путь в баги.


🧠 Помни: хорошо спроектированная схема ускоряет разработку, а не тормозит её.
А переделывать схему сложнее, чем сделать нормально с самого начала.

📲 Мы в MAX

#db

👉 @database_info
👍4🔥2
🔗 Мини-гайд по JOIN-ам в SQL

JOIN — мощнейший инструмент в арсенале SQL. Но многие путаются в типах. Разбираем на пальцах:


🔸 INNER JOIN

Что делает: оставляет только совпадающие строки из обеих таблиц.
Когда использовать: когда нужны только те записи, у которых есть соответствие.


SELECT *
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id;


🧠 Best practice: это дефолтный выбор. Работает быстрее, т.к. отбрасывает всё "лишнее".


🔸 LEFT JOIN

Что делает: возвращает все строки из левой таблицы и NULL из правой, если нет совпадения.
Когда использовать: когда нужно сохранить всё из первой таблицы, даже если во второй нет данных.


SELECT *
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;


🧠 Часто используется для аналитики: "у каких клиентов не было заказов?"


🔸 RIGHT JOIN

Что делает: наоборот — всё из правой таблицы и NULL из левой, если нет совпадения.
Когда использовать: аналогично LEFT JOIN, но редко встречается, потому что просто меняем порядок таблиц.


🔸 FULL OUTER JOIN

Что делает: объединяет LEFT и RIGHT — берёт всё из обеих таблиц.
Когда использовать: когда важны все данные, даже без соответствий.


SELECT *
FROM table_a
FULL OUTER JOIN table_b ON table_a.id = table_b.id;


🧠 Подходит для reconciliation'а или сверки.


Совет

Фильтры (WHERE) после LEFT JOIN могут не дать ожидаемый результат.
Используй ON для условий соединения, WHERE — для фильтрации результата.

Сохрани, чтобы не забыть. А ты какой JOIN используешь чаще всего?

📲 Мы в MAX

#db

👉 @database_info
4👍3👎1
🧱 Антипаттерн: Ненормализованная схема в SQL

Когда нужно «быстро запилить фичу», руки тянутся сделать одну таблицу, где и заказ, и клиент, и товары — всё в куче.

Пример:


CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_name TEXT,
customer_email TEXT,
product_1_name TEXT,
product_1_price NUMERIC,
product_2_name TEXT,
product_2_price NUMERIC
);


😰 Что пойдет не так:

Дублирование данных — имя клиента повторяется в каждом заказе.
Нет масштабируемости — максимум 2 продукта? А если будет 3?
Трудности с запросами — попробуй посчитать топ-5 товаров. Удачи.
Адские апдейты — изменить email клиента надо во всех заказах.

Как правильно:

1. Нормализуй. Раздели данные на сущности: customers, orders, products, order_items.
2. Используй внешние ключи.
3. Не бойся JOIN'ов — они для этого и придуманы.

Пример нормализованной структуры:


-- Таблица клиентов
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name TEXT,
email TEXT
);

-- Таблица заказов
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(id)
);

-- Таблица товаров
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT,
price NUMERIC
);

-- Связка товаров и заказов
CREATE TABLE order_items (
order_id INT REFERENCES orders(id),
product_id INT REFERENCES products(id),
quantity INT
);


📌 Да, нормализация требует чуть больше времени. Зато потом вы не утонете в хаосе.

Сохрани, чтобы не забыть — и не повторять чужих ошибок.

📲 Мы в MAX

#db

👉 @database_info
👍52🔥1
Мини-гайд: Как ускорить SELECT’ы в PostgreSQL с помощью покрытия индекса (covering index)

Иногда индекс есть, а запрос всё равно тормозит. Почему?

👉 Потому что обычный индекс помогает найти строку, но потом БД всё равно лезет в таблицу, чтобы достать нужные поля. Это называется heap access — и это дорого.

📌 Решение — покрывающий индекс. Это индекс, который содержит все нужные поля прямо в себе. PostgreSQL с версии 11 умеет делать это через INCLUDE.

🔧 Пример:


-- Запрос
SELECT name, email FROM users WHERE status = 'active';

-- Обычный индекс
CREATE INDEX idx_users_status ON users(status);

-- Покрывающий индекс
CREATE INDEX idx_users_status_cover ON users(status) INCLUDE (name, email);


Теперь PostgreSQL может ответить на запрос только по индексу, не трогая таблицу → быстрее.

📈 Особенно заметный профит:
– На больших таблицах
– В OLTP-нагрузках (много коротких запросов)
– Когда важна задержка ответа (например, API)

⚠️ Но не стоит включать весь ряд в индекс — это увеличит размер и замедлит обновления.

Вывод:
Покрывающие индексы — мощный способ ускорить SELECT без изменения запроса. Используй их там, где читаешь часто, а пишешь редко.

Сохрани, пригодится при оптимизации ⚙️

📲 Мы в MAX

#db

👉 @database_info
👍103
💣 NULL — тихий саботаж в твоей БД

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

📉 Антипаттерн: беспечное обращение с NULL

Примеры:


SELECT * FROM users WHERE age > 18; -- Пользователи с age = NULL не попадут


Ты думаешь, что отбираешь всех взрослых, но age = NULL тут "выпадает", ведь NULL > 18UNKNOWN.


WHERE col1 = col2 -- Не сработает, если хотя бы одно значение NULL


🙈 Проблема: NULL не равно даже самому себе (NULL != NULL).
📉 Итоги: ошибки в JOIN'ах, WHERE-фильтрах, расчетах.



🛡 Как защититься:

Явно учитывай NULL:


WHERE age > 18 OR age IS NULL -- если хочешь включить "неизвестный возраст"


Используй COALESCE:


SELECT COALESCE(discount, 0) FROM orders -- подставим 0, если скидка не указана


Проверяй NULL через IS NULL / IS NOT NULL

Для агрегаций учитывай поведение NULL:


AVG(column) -- пропустит NULL'ы, но COUNT(column) тоже не посчитает их!



Вывод:
NULL — не "ничего", а "неизвестно".
Пиши запросы так, как будто NULL всегда где-то прячется — и он не на твоей стороне.

Сохрани, чтобы не ловить грабли 💥

📲 Мы в MAX

#db

👉 @database_info
👍145
Разбор кейса. Компания переехала с MongoDB на PostgreSQL - зачем и что пошло не так.

Стартап хранил всё в MongoDB — быстро, удобно, JSON-документы летят.
Но через год — бизнес растёт, появляются проблемы:


🔸 Запросы тормозят.
Mongo не любит сложные агрегаты с джойнами по коллекциям.
А бизнесу уже нужно:
– аналитика по заказам
– ретеншн-отчёты
– CRM-связи между сущностями

🔸 Дублирование данных.
Документы растут, становятся вложенными, обновлять — боль.
Классическая проблема: “Обновили e-mail юзера — забыли в двух местах”.

🔸 Сложность поддержки.
Без схемы трудно отследить, что где лежит. Новым разработчикам — боль.


🔁 Решение: PostgreSQL

– Явная схема → валидируем данные сразу
– Поддержка JSONB → можно переехать частями
– Сильный SQL → отчёты, джойны, агрегации — на ура
– Надёжность и mature-инструменты для миграций, бэкапов, мониторинга


⚠️ Подводные камни:
– Миграция данных: пришлось писать парсеры и валидаторы
– Пришлось переосмыслить структуру: из “гибкого” хаоса в нормализованную модель
– Команда училась писать SQL и настраивать индексы


Зато теперь:
– Запросы летят
– Данные валидны
– Аналитика возможна
– Рост — без боли


Переход с NoSQL на SQL — это не “откат назад”, это осознанный апгрейд, когда бизнесу нужен контроль, скорость и предсказуемость.

📲 Мы в MAX

#db

👉 @database_info
👍102🔥1
PostgreSQL: архитектура и тюнинг SQL-запросов

Погрузись в архитектуру и прокачай оптимизацию запросов одной из самых популярных open source СУБД – PostgreSQL.

🌐 В программе курса:

🤩 Разберем, как работают СУБД вообще и PostgreSQL в частности: что такое MVCC, ACID, WAL, LRU, PPC/TPC и другие фундаментальные понятия архитектуры баз данных

🤩 Получите теорию и практику EXPLAIN и EXPLAIN ANALYZE на разных типа запросов: без индексов, с индексами, index only, нормализованные и документ-ориентированные данные и json-поля, изменение параметров сессии/конфигурации для ускорения запросов

🤩 Изучите архитектуру хранения данных в PostgreSQL, типы и особенности индексов, а также получите полезные советы и трюки оптимизации БД

🤩 Получите свой собственный выделенный облачный PostgreSQL-сервер (8 vCPU, 12G RAM, 100G NVMe) – предоставляется БЕСПЛАТНО на время обучения + готовый e-commerce датасет TPC-H (миллион пользователей, несколько миллионов заказов на десятки гигабайт)

🗓 Старт курса: 22 января. 5 недель обучения.

Изучить программу и записаться можно здесь.

🤩Кто мы: R&D-центр Devhands, основатель школы Алексей Рыбак. Автор курса — Николай Ихалайнен, эксперт по СУБД (ex-Percona), со-основатель MyDB, энтузиаст открытого ПО.

Реклама. ИП Рыбак А.А. ИНН 771407709607 Erid: 2VtzqwXjZhV
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥4👍3
PostgreSQL или MySQL?

Один из самых частых вопросов от разработчиков и DevOps - “Что лучше: PostgreSQL или MySQL?”. Давай без фанатизма, просто по фактам 👇

🔷 PostgreSQL:

🔵Поддержка JSONB с индексами - почти как NoSQL внутри SQL
🔵CTE, оконные функции, полнотекстовый поиск - топ для аналитики
🔵Расширяемость: можно писать свои типы, функции, операторы
🔵Хорош для сложных запросов, аналитики, геоданных (PostGIS)

🔻 Минусы:
– Сложнее в настройке и оптимизации
– Меньше хостингов out-of-the-box (но всё быстро меняется)


🔶 MySQL (особенно InnoDB / MariaDB):

🔵Быстрее на простых SELECT/INSERT, если запросы примитивные
🔵Больше ready-to-go хостингов и тулов для web
🔵Низкий порог входа - быстрее поднимается новичками

🔻 Минусы:
– Слабее в сложных SQL-конструкциях
– Нет нормальной поддержки CTE до недавнего времени
– JSON без индексации (в MySQL < 8.0)


Вывод:
🧠 Если делаешь CRM, веб-продукт или MVP с простыми запросами, MySQL зайдёт.

📊 Если строишь data-heavy приложения, BI, ETL или гео-системы, PostgreSQL без шансов.

📲 Мы в MAX

#db

👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5👌2🔥1
🛑 Антипаттерн: "Списки через запятую" в базе данных

Признайтесь, у каждого был соблазн сделать это. У вас есть сущность (например, User), и нужно сохранить список их ролей или IDs купленных товаров. Создавать отдельную таблицу кажется оверхедом, и вы решаете: "А, запишу просто строкой через запятую".

В БД это выглядит так:
role_ids: "1,4,12"

Почему это бомба замедленного действия и как это лечить? Давайте разбираться.

Почему это плохо (The Pain):

1. Сложный поиск. Найти всех пользователей с role_id = 1 через LIKE '%1%' - это больно. Вы найдете и 1, и 12, и 100. Придется писать монструозные регулярки.

2. Никаких индексов. База данных не может эффективно индексировать подстроки в таком формате. Full scan обеспечен.

3. Проблемы с JOIN. Вы не сможете сделать нормальный JOIN с таблицей ролей.

4. Целостность данных. Вы не можете повесить Foreign Key. Никто не помешает записать туда "1, 4, apple, NULL".

5. Атомарность обновлений. Удалить роль 4 из строки "1,4,12" - это чтение, парсинг на бекенде и перезапись. Состояние гонки (race condition) гарантировано.

Как делать правильно:

Вариант 1: Классическая нормализация (Junction Table)
Создайте связующую таблицу. Это золотой стандарт для реляционных БД (PostgreSQL, MySQL, Oracle).


-- Плохо
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
role_ids VARCHAR(255) -- "1,2"
);

-- Хорошо
CREATE TABLE user_roles (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);



Теперь выборка всех админов - это моментальный запрос с использованием индексов.

Вариант 2: Массивы или JSONB (PostgreSQL)
Если вы используете PostgreSQL и вам действительно не нужны жесткие FK (Foreign Keys) на каждый элемент, можно использовать нативные типы.


-- Допустимо в Postgres
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT,
role_ids INT[] -- массив целых чисел
);

-- Поиск (очень быстрый с GIN индексом):
SELECT * FROM users WHERE 1 = ANY(role_ids);



Никогда не храните списки в VARCHAR, если вам когда-либо придется искать по содержимому этого списка или джойнить его. Экономия 5 минут на старте обернется часами рефакторинга позже.

Сталкивались с таким в легаси-коде? Пишите в комменты 👇

📲 Мы в MAX

#db

👉 @database_info
👍64
Антипаттерн: SELECT * - удобно, но опасно

Использовать SELECT * - значит звать всех на вечеринку, даже если звал только двоих.

Почему это плохо:
🔹 Излишняя нагрузка на сеть и СУБД - выбираются все столбцы, включая ненужные.
🔹 Проблемы с индексами - СУБД может не использовать покрывающий индекс.
🔹 Ломается при изменении схемы - добавил столбец → внезапно изменилось поведение приложения.
🔹 Сложнее читать и поддерживать - особенно в JOIN’ах.

Как правильно:
Запрашивай только нужные поля:


SELECT id, name, created_at FROM users;


📌 И даже в админках/аналитике лучше явно указывать поля - это дисциплинирует.

Хочешь писать код, который легко масштабировать и отлаживать - забудь про SELECT *.

Сохрани, чтобы не забыть 💾
Поделись с коллегами, которые всё ещё "звёздят" в SQL

📲 Мы в MAX

#db

👉 @database_info
👍8
🔧 Mini-гайд: ускоряем JOIN-ы в больших таблицах

JOIN-ы - мощный инструмент SQL, но на больших объёмах данных могут стать узким горлышком. Вот 5 проверенных способов ускорить их:


1. Индексы по ключам соединения
Без индекса - каждый JOIN превращается в полный перебор.

Пример:


CREATE INDEX idx_user_id ON orders(user_id);


2. Ограничь объём данных до JOIN-а
Фильтруй и агрегируй данные до объединения.

Вместо:


SELECT * FROM orders o JOIN users u ON o.user_id = u.id WHERE u.country = 'DE';


Лучше:


WITH german_users AS (
SELECT id FROM users WHERE country = 'DE'
)
SELECT * FROM orders o JOIN german_users g ON o.user_id = g.id;


3. Учитывай тип JOIN-а
INNER JOIN обычно быстрее OUTER JOIN, особенно при наличии NOT NULL. Иногда EXISTS работает быстрее, чем LEFT JOIN.

4. Следи за типами данных
JOIN по полям с разными типами (например, int и varchar) = неэффективный cast + тормоза.

5. Проверь планы выполнения (EXPLAIN)
Не гадай, а смотри, что реально происходит. EXPLAIN ANALYZE - твой друг.


📌 Даже один лишний JOIN может уронить производительность. Внимательность + EXPLAIN = уверенность.

Поделись с коллегами - спасёшь чей-то прод.

📲 Мы в MAX

#db

👉 @database_info
👍9
Антипаттерн: булевы значения как строки

В таблице users встречал такое:


is_active VARCHAR(5) -- значения 'true' или 'false'


На первый взгляд — ерунда. На практике:
– нет валидации: можно вставить 'tru', 'yes', '0',
– медленнее сравнение, чем у BOOLEAN,
– больше места в хранилище,
– сложно агрегировать и строить аналитику.

🔧 Как надо:


is_active BOOLEAN DEFAULT true


– Экономия места (1 байт против 5 и больше)
– Проверка через WHERE is_active
– Простой COUNT(*) FILTER (WHERE is_active) для отчётов
– Автоматическая поддержка в ORM и UI-форматах

📌 Даже если тебе нужно больше состояний - используй ENUM, а не строку.

💡Чем проще тип, тем меньше шансов на баг.
Сохрани, если в коде встречал такое - и переделай с чистой совестью.

📲 Мы в MAX

#db

👉 @database_info
👍10🔥1
Индексы в PostgreSQL: когда и как ставить, чтобы ускорить запросы

🔍 Что такое индекс?
Индекс в PostgreSQL - это структура данных (обычно B-tree), позволяющая быстро находить строки по значению столбца, не сканируя всю таблицу.


⚙️ Пример создания простого B-tree-индекса


-- Ускоряем поиск по полю email
CREATE INDEX idx_users_email
ON users (email);



Best Practices

1. Выбирай правильный тип

🔹 BTREE - по умолчанию, для большинства операций сравнения (=, <, >, BETWEEN).
🔹GIN/GiST - для полнотекстового поиска (tsvector), работы с массивами и геоданных.
🔹HASH - для строго равенств (=), но редко нужен.

2. Индексируй часто фильтруемые и сортируемые поля

🔹WHERE, JOIN, ORDER BY.
🔹Например, для запросов типа


SELECT * FROM orders
WHERE user_id = 42
ORDER BY created_at DESC;


создаём составной индекс:


CREATE INDEX idx_orders_user_created
ON orders (user_id, created_at DESC);


3. Не злоупотребляй

🔹Каждый индекс занимает место и замедляет INSERT/UPDATE/DELETE.
🔹Проанализируй pg_stat_user_indexes и pg_stat_user_tables через pg_stat_statements перед добавлением.

4. Используй частичные индексы
Если условие фильтрации редко меняется, можно сузить индекс:


CREATE INDEX idx_active_users_email
ON users (email)
WHERE active = true;


5. Поддерживай актуальность
Периодически делай REINDEX или VACUUM ANALYZE для больших таблиц, чтобы индекс не фрагментировался.


Антипаттерн: “Индекс на всё”
Создание индекса на каждый столбец:


-- Плохо: много маленьких индексов, мало пользы, много затрат
CREATE INDEX idx1 ON table(a);
CREATE INDEX idx2 ON table(b);
CREATE INDEX idx3 ON table(c);
...


Проблемы:

🔹Большой объём хранилища.
🔹Замедление DML-операций.
🔹Планы запросов могут пропускать некоторые индексы.


💡 Вывод
Правильно подобранные и настроенные индексы - ключ к быстрой работе базы. Сосредоточься на реально востребованных столбцах, комбинируй, не забывай про обслуживание.

Сохрани этот мини-гайд, чтобы не забыть, и поделись с коллегами: какие индексы стали для тебя открытием?

📲 Мы в MAX

#db

👉 @database_info
👍7