SQL Portal | Базы Данных
14K subscribers
982 photos
135 videos
51 files
739 links
Присоединяйтесь к нашему каналу и погрузитесь в мир баз данных

Связь: @devmangx

РКН: https://clck.ru/3H4Wo3
Download Telegram
Любимый паттерн для “последняя запись на группу” в Postgres — DISTINCT ON.
Да, он Postgres-specific. Нет, он не из SQL Standard. Зато для этой задачи он читается почти идеально.

Есть таблица заказов:
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id int,
status text,
created_at timestamptz
);


Старый классический вариант:
SELECT o.*
FROM orders o
INNER JOIN (
SELECT customer_id, MAX(created_at) AS latest
FROM orders
GROUP BY customer_id
) latest ON o.customer_id = latest.customer_id
AND o.created_at = latest.latest;


Работает, но есть нюансы.
Таблица читается два раза: сначала ищем MAX(created_at) по каждому customer_id, потом джойнимся обратно за полной строкой.

И ещё хуже: если у одного клиента два заказа с одинаковым created_at, запрос может вернуть две строки. Нужен tie-breaker.

В Postgres проще так:
SELECT DISTINCT ON (customer_id)
customer_id, status, created_at
FROM orders
ORDER BY customer_id, created_at DESC;


DISTINCT ON (customer_id)
оставляет первую строку для каждого клиента после сортировки.
То есть ORDER BY customer_id, created_at DESC сначала кладёт свежий заказ клиента наверх, а DISTINCT ON забирает его.

Чтобы это реально летало, добавляем индекс:
CREATE INDEX ON orders (customer_id, created_at DESC);


Теперь Postgres может идти по уже отсортированному индексу и не делать отдельную сортировку.
На небольшом тесте с 100k строк и 20k customer_id получилось так:
Без индекса:
MAX + self-join: 32.92ms
DISTINCT ON: 34.75ms
ROW_NUMBER(): 38.68ms
С индексом (customer_id, created_at DESC):
DISTINCT ON: 15.99ms
ROW_NUMBER(): 25.36ms
MAX + self-join: 38.01ms

Главный вывод: для “одна последняя строка на группу” в Postgres я первым беру DISTINCT ON.
Если нужно не одну строку, а top N строк на группу, тогда уже ROW_NUMBER():
SELECT customer_id, status, created_at
FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY created_at DESC
) AS rn
FROM orders
) ranked
WHERE rn = 1;


DISTINCT ON
— для простого “последний на группу”.
Window functions — когда нужна гибкость.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Чем больше пользуюсь Claude Code, тем сильнее кажется, что главный навык теперь не кодинг.

А управление агентами.

У некоторых workflow выглядит примерно так:

Одна задача автоматически разбивается между несколькими агентами.

• один накидывает идеи
• один пишет технический план
• один кодит
• один делает ревью
• один ищет проблемы и пропущенные кейсы

Всё складывается в markdown, чтобы не потерять контекст.

Да, получается медленнее.

Да, приходится ждать.

Но качество обычно выше, потому что каждый агент делает только одну работу.

А настоящий буст начинается с Git Worktrees.

Вместо одной сессии Claude Code можно держать несколько параллельных.

Одна пилит новую фичу.

Вторая разбирается с багом.

Третья пишет тесты.

Четвёртая занимается рефакторингом.

В итоге начинаешь думать не в стиле «как написать этот код», а в стиле «как распределить работу между агентами и не потерять контроль над результатом».

Похоже, это и есть новый engineering skill.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Тебе точно понравится:

Этот инструмент, который преобразует запросы на естественном языке в SQL-запросы

То есть пишешь ему, например:
все книги которые взяли студенты 2 курса за последнюю неделю

И он вам выдаёт SELECT * FROM ... и тд.

Очень удобный инструмент для тех, кто не знаком с SQL, но нуждается в работе с базами данных, пользуйтесь 📸

Ознакомиться: тут

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Новый бесплатный курс от Anthropic!

Agent Skills: пошагово и с нуля
✓ Создание навыков (Skills) и лучшие практики
✓ Отличия от Tools и MCP
✓ Для Claude Code и Visual Studio Code
2 часа контента и 10 видеоуроков

deeplearning.ai/short-courses/agent-skills-with-anthropic/

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
Лето начинается: водные развлечения, гриль на острове и новые маршруты в бутик-отеле «Заонежье»

Для гостей бутик-отеля «Заонежье» к новому сезону подготовлены лесные трассы для первых заездов на новых квадроциклах — они позволяют исследовать дикие уголки карельской тайги и скалистые берега озер. Велопрогулки по дорогам Заонежья стали еще комфортнее благодаря электробайкам, которые этой весной пополнили велопарк отеля.

В мае, в отеле открылся сезон водных развлечений: гостям доступны сапы, каяки и весельные лодки. В этом году пользование ими - комплементарное для всех гостей. Также появились новые маршруты для катеров по заливам Онежского озера.

Ресторан «Руна» представляет летнюю серию гриль-сетов, которые можно приготовить самостоятельно на одном из необитаемых островов или устроить барбекю на лужайке у дома. В меню — овощные и рыбные сеты, а также мясное ассорти.

Бутик-отель «Заонежье» приглашает к активному отдыху среди озер и северной природы Карелии.
😁1
Начиная с Oracle 21c, массивы PL/SQL можно инициализировать через конструкторы с циклом FOR.

array := arr_type ( FOR i IN ... )


Такие конструкторы позволяют перебирать:
значения — IN x .. y
результат курсора — IN ( SELECT ... )
Для задания собственных индексов можно использовать необязательное выражение INDEX.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Появился /no-mistakes.

По многочисленным просьбам автор вынес один из самых полезных инструментов своего агентного пайплайна в отдельный skill для Claude Code, Codex и других агентных сред.
После того как агент внёс изменения, достаточно выполнить: /no-mistakes

Дальше инструмент автоматически проверит изменения и поможет найти проблемы до коммита.
По словам автора, код, сгенерированный ИИ, даже лучшими моделями, пока нельзя безоговорочно принимать и мержить без тщательной проверки.

Его собственная статистика:
68% изменений содержали проблемы
эти проблемы могли попасть в основную ветку, если бы их не обнаружил no-mistakes

Раньше инструмент запускался только через:
git push no-mistakes

Теперь доступен и как skill.
Исходный код открыт и распространяется бесплатно: https://github.com/kunchenguid/no-mistakes

Для настройки в репозитории:
no-mistakes init

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
😁1
«Но ведь запрос был быстрым на dev и staging!»

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

Есть две точки, после которых простой запрос начинает заметно замедляться:

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

Примеры ниже показаны на небольших стендах с ограниченными ресурсами. В крупных системах картина будет такой же, только с гораздо большим количеством строк.
Простой запрос:
SELECT user_id, event_type, created_at
FROM events
WHERE user_id = 1
ORDER BY created_at DESC


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

(Небольшая оговорка: если для пользователя нет ни одной строки, PostgreSQL может решить вообще не сканировать таблицу, используя статистику по таблице и колонкам.)

Фаза 1. Всё помещается в память

Таблица содержит 10 000 строк.
Для user_id = 1 найдено 5 000 событий.
work_mem = 1MB.

Что видно ((фото1):

Sort Method: quicksort Memory: 427kB
Все 5 000 строк были отсортированы прямо в RAM.
Rows Removed by Filter: 5000
PostgreSQL всё равно прочитал остальные 5 000 строк и отбросил их.
Buffers: shared hit=74
Все страницы уже находились в памяти (shared_buffers).

Фаза 2. Сортировка начинает писать на диск

Теперь в таблице 200 000 строк.
У пользователя уже 100 000 событий.
work_mem всё ещё равен 1 MB.

Что изменилось (2):

Sort Method: external merge Disk: 3352kB
Сортировка больше не помещается в память и начинает использовать временные файлы.
temp read=836 written=843
PostgreSQL записал 843 временные страницы на диск и затем прочитал их обратно во время merge-фазы.
Rows Removed by Filter: 100000
Ещё 100 тысяч строк были прочитаны только для того, чтобы потом их выбросить.

Фаза 3. Таблица перестаёт помещаться в буферный кеш

Всего уже 600 000 строк.
Из них 100 000 принадлежат нужному пользователю, остальные 500 000 — другим пользователям.
PostgreSQL всё равно вынужден читать их.
Размер таблицы становится больше shared_buffers.

(3 фото)

Планировщик запустил 2 параллельных воркера.
Сканирование и сортировка были распределены между 3 процессами, поэтому каждый сортировал примерно по 33 000 строк вместо 100 000.

Но основные проблемы остались:

shared read=3049
Таблица перестала помещаться в буферный кеш. Более 3 000 страниц пришлось читать с диска.
Rows Removed by Filter: 166667 × 3
Было прочитано и выброшено около 500 тысяч строк.
Все три сортировки всё равно использовали external merge
Параллелизм уменьшил объём работы на каждый процесс, но не убрал запись на диск.

Решение зависит от конкретного приложения.
Самый очевидный вариант:
CREATE INDEX idx_events_user_created_at
ON events (user_id, created_at DESC);


Такой индекс позволяет резко сократить объём сортировки и избежать полного сканирования таблицы.
Но индекс далеко не единственный вариант.
В зависимости от нагрузки могут помочь:
- кэширование на уровне приложения;
- materialized views;
- партиционирование таблиц;
- изменение модели хранения данных.

Главный вывод простой:

Если в EXPLAIN ANALYZE появляются:
external merge
temp read / temp written
большие значения Rows Removed by Filter
shared read

то запрос, который отлично работал на dev-базе с 10 тысячами строк, уже начинает показывать, что будет происходить в production на миллионах записей.

👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31