Как вернуть строки в том же порядке, в котором пришли 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
В системах с временными интервалами важно понимать не просто отдельные события, а их наложение — именно оно определяет реальную нагрузку.
Сегодня в задаче:
• Преобразуем интервалы в точки начала и конца, чтобы работать с ними как с потоком событий;
• Посчитаем текущую нагрузку через накопительную сумму по времени;
• Найдём момент максимального количества одновременных событий — пик нагрузки системы.
Этот приём используется в мониторинге, аналитике, планировщиках и системах, где важно контролировать параллельную активность.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍8🔥7🤝4
This media is not supported in your browser
VIEW IN TELEGRAM
Здесь публикуются ежедневные статьи, обучающие серии Stairway, подборки скриптов, обзоры книг, а также активные форумы и блоги для администраторов БД и разработчиков.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍9🔥8🤝2
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🤝9❤7🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Если хочется быстро разобраться в командах — этот сайт отлично подойдёт. Конструкции объяснены на примерах, с чётким и кратким синтаксисом. Удобно использовать как справочник или экспресс‑повторение.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15🔥8👍7🤝3
Например, Primary Key гарантирует уникальность записей, Foreign Key — обеспечивает ссылочную целостность, а JOIN’ы позволяют собирать распределённые данные в единую выборку.
На картинке — основные концепции, которые используются при проектировании и работе с БД.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤8🤝8
Проверяем дубликаты и считаем уникальные значения!
В больших таблицах важно быстро находить повторяющиеся записи и понимать, сколько уникальных элементов. Это полезно для контроля качества данных и аналитики.
Создадим таблицу пользователей:
Запрос для выявления дубликатов и подсчёта уникальных email:
Функция
Результат:
🔥 Это простой способ контролировать качество данных, выявлять ошибки и готовить отчёты для команды.
➡️ SQL Ready | #практика
В больших таблицах важно быстро находить повторяющиеся записи и понимать, сколько уникальных элементов. Это полезно для контроля качества данных и аналитики.
Создадим таблицу пользователей:
CREATE TABLE users (
user_id INT,
email VARCHAR(100)
);
INSERT INTO users VALUES
(1, 'alice@mail.com'),
(2, 'bob@mail.com'),
(3, 'alice@mail.com'),
(4, 'carol@mail.com'),
(5, 'bob@mail.com');
Запрос для выявления дубликатов и подсчёта уникальных email:
SELECT email, COUNT(*) AS cnt,
CASE WHEN COUNT(*)>1 THEN 'Duplicate' ELSE 'Unique' END AS status
FROM users
GROUP BY email;
Функция
COUNT() + GROUP BY группирует одинаковые значения, а CASE сразу классифицирует их как дубликаты или уникальные.Результат:
email | cnt | status
-----------------------------
alice@mail.com | 2 | Duplicate
bob@mail.com | 2 | Duplicate
carol@mail.com | 1 | Unique
🔥 Это простой способ контролировать качество данных, выявлять ошибки и готовить отчёты для команды.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🔥7🤝6❤4
Использование data-modifying CTE для цепочек операций в одном запросе!
Обычно изменения и чтение делают разными запросами, что создаёт лишние
Теперь можно атомарно обновить данные и сразу записать аудит, отправить в очередь или выполнить дополнительную логику без повторных
🔥
➡️ SQL Ready | #совет
Обычно изменения и чтение делают разными запросами, что создаёт лишние
round-trip и риск рассинхронизации данных между операциями:UPDATE orders
SET status = 'processing'
WHERE id = 123
RETURNING *;
RETURNING уже даёт доступ к изменённым строкам, но data-modifying CTE позволяет пойти дальше и использовать их в следующих шагах:WITH updated AS (...)
CTE фиксирует результат изменения и гарантирует, что последующие операции работают с тем же набором строк в рамках одного statement:INSERT INTO audit_log (order_id, new_status)
SELECT id, status
FROM updated;
Теперь можно атомарно обновить данные и сразу записать аудит, отправить в очередь или выполнить дополнительную логику без повторных
SELECT.data-modifying CTE — это способ строить сложные, но безопасные цепочки операций внутри одного SQL-запроса.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥10🤝8