This media is not supported in your browser
VIEW IN TELEGRAM
Репозиторий с материалами для изучения SQL с самого начала: здесь разбираются основы работы с базами данных, синтаксис запросов, JOIN-ы, фильтрация, агрегации и структура таблиц. Формат обучения построен вокруг практики, примеры запросов, задания и объяснения помогают не просто читать теорию, а сразу писать код.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
❤21🔥10🤝10
This media is not supported in your browser
VIEW IN TELEGRAM
Здесь собраны конспекты, шпаргалки и учебные PDF по всем ключевым темам: от базовых запросов до сложных конструкций и оптимизации. Отдельно выделяется наличие большого количества материалов для подготовки к собеседованиям, сотни вопросов и разборов.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤7🔥7🤝2
UPDATE ... FROM в PostgreSQL: как обновлять данные из другой таблицы и не поймать скрытые дубли!
Задача, которая встречается постоянно: обновить таблицу по данным из другой. Например, проставить клиентам дату последнего заказа.
Есть таблицы:
Интуитивный вариант:
Запрос выполнится, но если у клиента несколько заказов — возникает вопрос: какая именно дата попадёт в
Запрос корректен синтаксически, но логически небезопасен. Если
Поэтому сначала нужно свести соответствие к одной строке на клиента. Через агрегат:
Теперь для каждого клиента ровно одна строка — обновление становится детерминированным.
Вариант через
Здесь тоже выбирается одна строка на клиента за счёт сортировки. Если возможны одинаковые
Коррелированный подзапрос:
Плюс: гарантированно одно значение. Нюанс: обновятся все строки в
Пример ловушки:
С виду безопасно, по факту — та же проблема: соответствие не уникально.
Практическая проверка: замените
Если дубли есть — такой
Помогает и для
🔥 Если
➡️ SQL Ready | #практика
Задача, которая встречается постоянно: обновить таблицу по данным из другой. Например, проставить клиентам дату последнего заказа.
Есть таблицы:
customers(id, last_order_at)
orders(id, customer_id, created_at)
Интуитивный вариант:
UPDATE customers c
SET last_order_at = o.created_at
FROM orders o
WHERE o.customer_id = c.id;
Запрос выполнится, но если у клиента несколько заказов — возникает вопрос: какая именно дата попадёт в
last_order_at? Ответ — это не гарантируется.Запрос корректен синтаксически, но логически небезопасен. Если
JOIN даёт несколько строк для одной записи в customers, PostgreSQL не гарантирует, какую строку он использует при обновлении.Поэтому сначала нужно свести соответствие к одной строке на клиента. Через агрегат:
UPDATE customers c
SET last_order_at = t.max_created_at
FROM (
SELECT
customer_id,
MAX(created_at) AS max_created_at
FROM orders
GROUP BY customer_id
) t
WHERE t.customer_id = c.id;
Теперь для каждого клиента ровно одна строка — обновление становится детерминированным.
Вариант через
DISTINCT ON:UPDATE customers c
SET last_order_at = t.created_at
FROM (
SELECT DISTINCT ON (customer_id)
customer_id,
created_at
FROM orders
ORDER BY customer_id, created_at DESC
) t
WHERE t.customer_id = c.id;
Здесь тоже выбирается одна строка на клиента за счёт сортировки. Если возможны одинаковые
created_at, лучше добавить tie-breaker:ORDER BY customer_id, created_at DESC, id DESC
Коррелированный подзапрос:
UPDATE customers c
SET last_order_at = (
SELECT MAX(o.created_at)
FROM orders o
WHERE o.customer_id = c.id
);
Плюс: гарантированно одно значение. Нюанс: обновятся все строки в
customers, и у клиентов без заказов будет NULL.Пример ловушки:
UPDATE customers c
SET last_order_at = t.created_at
FROM (
SELECT customer_id, created_at
FROM orders
) t
WHERE t.customer_id = c.id;
С виду безопасно, по факту — та же проблема: соответствие не уникально.
Практическая проверка: замените
UPDATE на SELECT с тем же JOIN и посмотрите, есть ли дубли:SELECT c.id, t.*
FROM customers c
JOIN ...
Если дубли есть — такой
UPDATE уже небезопасен. Про индексы:CREATE INDEX idx_orders_customer_created
ON orders (customer_id, created_at);
Помогает и для
MAX, и для ORDER BY. Итог: UPDATE ... FROM — крутой инструмент, но он не проверяет однозначность соответствия.JOIN возвращает несколько строк на одну обновляемую запись, результат не гарантируется. Сначала фиксируем одну строку на ключ — потом обновляем.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13❤9👍9
Например,
WHERE фильтрует строки до агрегации, а HAVING — уже после GROUP BY.На картинке — основные конструкции: разница между
WHERE и HAVING, оконные функции против обычной агрегации, а также основы партицирования для больших таблиц.Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥9🤝8❤1
Индексы и ORDER BY DESC без лишней сортировки!
Многие думают, что для
В PostgreSQL обычный B-tree индекс:
уже может использоваться для
Если нужен смешанный порядок (например (
🔥 В таких случаях PostgreSQL сможет читать данные сразу в нужном порядке.
➡️ SQL Ready | #совет
Многие думают, что для
ORDER BY … DESC нужен отдельный DESC-индекс — но это не всегда так.В PostgreSQL обычный B-tree индекс:
CREATE INDEX idx_orders_user_created
ON orders (user_id, created_at);
уже может использоваться для
ORDER BY created_at DESC через backward scan, без дополнительной сортировки.Если нужен смешанный порядок (например (
user_id ASC, created_at DESC)), тогда имеет смысл явно указать направление:CREATE INDEX idx_orders_user_created_desc
ON orders (user_id, created_at DESC);
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16👍11❤10
This media is not supported in your browser
VIEW IN TELEGRAM
Это AI-ассистент для обучения, который позволяет загружать документы, видео или аудио и автоматически превращать их в структурированные материалы: краткие конспекты, ответы, карточки и тесты. Также можно задавать вопросы прямо по загруженному источнику и получать точные ответы на основе его содержания.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥8🤝8
Уникальность с NULL без триггеров и костылей!
Обычный
Если колонка опциональная, но по смыслу значение всё равно должно быть уникальным, стандартный
Эта форма говорит PostgreSQL считать
То есть второй
Особенно полезно для
🔥
➡️ SQL Ready | #совет
Обычный
UNIQUE в SQL пропускает несколько NULL, потому что NULL не считается равным другому. Из-за этого ограничение часто формально есть, а правило на самом деле не соблюдается:UNIQUE (telegram_id)
Если колонка опциональная, но по смыслу значение всё равно должно быть уникальным, стандартный
UNIQUE даёт дыру в данных.UNIQUE NULLS NOT DISTINCT (telegram_id)
Эта форма говорит PostgreSQL считать
NULL обычным сравнимым значением именно для проверки уникальности.То есть второй
NULL уже не пройдёт, как и дубликат обычного значения.UNIQUE NULLS NOT DISTINCT (tenant_id, external_id)
Особенно полезно для
nullable внешних идентификаторов, one-to-one связей, интеграционных ключей и любых полей, где NULL тоже должен быть единственным допустимым состоянием.UNIQUE NULLS NOT DISTINCT закрывает один из источников грязных данных и заменяет триггеры.Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤8🔥8
Каждый уровень играет важную роль: от физической передачи сигналов до приложений, с которыми мы взаимодействуем каждый день. Понимание этой модели помогает лучше разбираться в сетевых ошибках, маршрутизации и защите данных.
На картинке — 7 уровней OSI, что делает каждый из них и примеры протоколов.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥7🤝7
Проверка качества и целостности данных!
В больших продакшн-базах важно не только находить ошибки, но и структурировать их: проверять NULL, дубликаты, некорректные форматы и аномальные значения.
Сначала выявляем строки с пустыми ключевыми полями:
Проверяем дубликаты по уникальному полю и сразу классифицируем их:
Ищем аномалии в числовых полях (например, сумма заказа < 0):
🔥 Это позволяет отслеживать качество данных, предотвращать ошибки аналитики и готовить отчёты для команды разработки.
➡️ SQL Ready | #практика
В больших продакшн-базах важно не только находить ошибки, но и структурировать их: проверять NULL, дубликаты, некорректные форматы и аномальные значения.
Сначала выявляем строки с пустыми ключевыми полями:
SELECT user_id, email, created_at
FROM users
WHERE user_id IS NULL
OR email IS NULL;
Проверяем дубликаты по уникальному полю и сразу классифицируем их:
SELECT email, COUNT(*) AS cnt,
CASE WHEN COUNT(*)>1 THEN 'Duplicate' ELSE 'Unique' END AS status
FROM users
GROUP BY email;
Ищем аномалии в числовых полях (например, сумма заказа < 0):
SELECT order_id, total_amount
FROM orders
WHERE total_amount < 0;
🔥 Это позволяет отслеживать качество данных, предотвращать ошибки аналитики и готовить отчёты для команды разработки.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍9🤝9
This media is not supported in your browser
VIEW IN TELEGRAM
Если нужно быстро освежить синтаксис или понять суть команд — это то, что нужно. Все основные конструкции, примеры и видеоуроки — коротко и по делу. Отлично подойдёт как шпаргалка и мини‑курс.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥8🤝8❤1
FILTER позволяет задать условие прямо для SUM, COUNT, AVG — без вложенных подзапросов и лишнего шума. Код получается чище, короче и проще читается.Что важно знать:
• FILTER работает внутри агрегата — условие применяется только к нему.
• Отлично подходит для отчётных таблиц с множеством условий.
• Заменяет CASE WHEN в 90% ситуаций, где раньше казалось без него никак.
Поэтому, это инструмент, с которым SQL-запросы становятся короче и понятнее.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍10❤9🤝2
This media is not supported in your browser
VIEW IN TELEGRAM
Универсальная шпаргалка по SQL, где собраны все основные конструкции языка. Всё структурировано по разделам, поэтому можно быстро найти нужный синтаксис. Формат максимально практичный: команда, пример, объяснение, что позволяет не просто смотреть, а сразу понимать, как что применяется в запросах.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝11👍8🔥8❤2
Как вернуть строки в том же порядке, в котором пришли id?
Когда из приложения прилетает список id, обычный
Такой запрос вернёт правильный набор строк, но порядок будет таким, как решит планировщик, а не таким, как пришёл список.
Дальше это уже обычная таблица, которую можно джойнить, фильтровать и сортировать:
В итоге база сама возвращает строки в нужном порядке, без
🔥
➡️ SQL Ready | #совет
Когда из приложения прилетает список id, обычный
WHERE id = ANY(...) находит нужные строки, но порядок входного массива не сохраняет:SELECT *
FROM users
WHERE id = ANY(ARRAY[42, 7, 99]);
Такой запрос вернёт правильный набор строк, но порядок будет таким, как решит планировщик, а не таким, как пришёл список.
WITH ORDINALITY добавляет каждой строке её позицию во входном наборе:unnest(ARRAY[42, 7, 99]) WITH ORDINALITY
Дальше это уже обычная таблица, которую можно джойнить, фильтровать и сортировать:
ORDER BY x.ord
В итоге база сама возвращает строки в нужном порядке, без
CASE, без ручной сортировки в коде и без лишнего постобработчика.WITH ORDINALITY — это простой способ сохранить порядок входных данных в SQL, он хорошо заходит в API и массовые выборки.Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥9🤝6❤2
Optimistic locking позволяет работать без блокировок — конфликт проверяется при записи (например, через
version или updated_at). Pessimistic locking наоборот сразу ставит блокировку (SELECT ... FOR UPDATE) и заставляет другие транзакции ждать.На картинке — как два подхода ведут себя при одновременном обновлении одной строки: в одном случае получаем conflict, в другом — очередь.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥9👍8🤝3
Почему после JOIN внезапно растут агрегаты!
Одна из самых неприятных ошибок в аналитическом SQL — когда запрос выглядит нормально, всё отрабатывает без ошибок, а цифры в итоге получаются больше, чем должны быть.
Классическая ситуация. Есть таблицы:
Допустим, хотим посчитать сумму заказов. На первый взгляд кажется, что такой запрос окей:
Но тут и начинается подвох.
Если у одного заказа 3 позиции в
Быстрая проверка, есть ли проблема:
Если строк после
Если вам нужна просто сумма по заказам, то
Если
Почему это хорошо: гранулярность
Ещё нормальный вариант — сначала убрать дубли на стороне
Тут мы заранее приводим данные к уровню одна строка = один заказ, и только потом джойним.
А если задача вообще на уровне позиций, например нужно посчитать общее количество товаров, тогда считать надо уже по
То есть важный момент очень простой: агрегировать нужно на том уровне, где реально живёт ваша метрика.
Отдельно про популярный костыль:
С виду кажется, что
Ещё неприятнее, когда
Практический способ быстро это поймать — смотреть количество строк после каждого шага:
Так обычно сразу видно, на каком
🔥 Итог простой: перед тем как писать
➡️ SQL Ready | #практика
Одна из самых неприятных ошибок в аналитическом SQL — когда запрос выглядит нормально, всё отрабатывает без ошибок, а цифры в итоге получаются больше, чем должны быть.
Классическая ситуация. Есть таблицы:
orders(id, customer_id, amount)
order_items(id, order_id, product_id, quantity)
Допустим, хотим посчитать сумму заказов. На первый взгляд кажется, что такой запрос окей:
SELECT SUM(o.amount) AS total_revenue
FROM orders o
JOIN order_items i
ON i.order_id = o.id;
Но тут и начинается подвох.
Если у одного заказа 3 позиции в
order_items, то строка из orders после JOIN повторится 3 раза. И o.amount тоже попадёт в расчёт 3 раза. В итоге сумма завышается. Это как раз тот самый fan-out: одна строка размножается после JOIN.Быстрая проверка, есть ли проблема:
SELECT
COUNT(*) AS rows_after_join,
COUNT(DISTINCT o.id) AS unique_orders
FROM orders o
JOIN order_items i
ON i.order_id = o.id;
Если строк после
JOIN стало больше, чем уникальных заказов, значит у вас fan-out по уровню orders. Что делать правильно — зависит от задачи.Если вам нужна просто сумма по заказам, то
JOIN вообще не нужен:SELECT SUM(amount)
FROM orders;
Если
JOIN нужен только для фильтрации, например по конкретному товару, безопаснее использовать EXISTS:SELECT SUM(o.amount)
FROM orders o
WHERE EXISTS (
SELECT 1
FROM order_items i
WHERE i.order_id = o.id
AND i.product_id = 10
);
Почему это хорошо: гранулярность
orders не ломается. Один заказ остаётся одной строкой.Ещё нормальный вариант — сначала убрать дубли на стороне
order_items:SELECT SUM(o.amount)
FROM orders o
JOIN (
SELECT DISTINCT order_id
FROM order_items
WHERE product_id = 10
) i ON i.order_id = o.id;
Тут мы заранее приводим данные к уровню одна строка = один заказ, и только потом джойним.
А если задача вообще на уровне позиций, например нужно посчитать общее количество товаров, тогда считать надо уже по
order_items:SELECT SUM(i.quantity)
FROM order_items i;
То есть важный момент очень простой: агрегировать нужно на том уровне, где реально живёт ваша метрика.
Отдельно про популярный костыль:
SELECT SUM(DISTINCT o.amount)
FROM orders o
JOIN order_items i
ON i.order_id = o.id;
С виду кажется, что
DISTINCT сейчас всё починит, на деле — нет. Почему это плохая идея: если у двух разных заказов одинаковый amount, один из них просто схлопнется; запрос начинает давать вроде бы правдоподобный, но неверный результат.Ещё неприятнее, когда
JOIN не один, а цепочка: orders — order_items — products — categoriesПрактический способ быстро это поймать — смотреть количество строк после каждого шага:
SELECT COUNT(*) FROM orders;
SELECT COUNT(*)
FROM orders
JOIN order_items ON order_items.order_id = orders.id;
SELECT COUNT(*)
FROM orders
JOIN order_items ON order_items.order_id = orders.id
JOIN products ON products.id = order_items.product_id;
Так обычно сразу видно, на каком
JOIN начинаются лишние строки.JOIN, всегда держите в голове гранулярность данных. Что у вас является одной строкой: заказ, позиция заказа, клиент? Сначала определяете уровень данных — потом джойните и агрегируете.Please open Telegram to view this post
VIEW IN TELEGRAM
❤13🔥10👍8🤝2
В этой статье:
• Показано, как в YDB обрабатывается SQL-запрос — от парсинга до распределённого исполнения по узлам;
• Разбирается, как устроены планировщик, оптимизатор и механизмы шардинга при работе с большими данными;
• Объясняется, как достигаются консистентность, отказоустойчивость и масштабируемость в распределённой базе.🔊 Продолжайте читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥7👍6🤝3