SQL Ready | Базы Данных
Авторский канал про Базы Данных и SQL Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Cотрудничество: @energy_c РКН: https://clck.ru/3QREBc
إظهار المزيد📈 نظرة تحليلية على قناة تيليجرام SQL Ready | Базы Данных
تُعد قناة SQL Ready | Базы Данных (@sql_ready) في القطاع اللغوي الروسية لاعباً نشطاً. يضم المجتمع حالياً 17 719 مشتركاً، محتلاً المرتبة 7 336 في فئة التكنولوجيات والتطبيقات والمرتبة 37 136 في منطقة روسيا.
📊 مؤشرات الجمهور والحراك
منذ تأسيسه في невідомо، حقق المشروع نمواً سريعاً وجمع 17 719 مشتركاً.
بحسب آخر البيانات بتاريخ 18 أكتوبر, 2026، تحافظ القناة على نشاط مستقر. خلال آخر 30 يوماً تغيّر عدد الأعضاء بمقدار 696، وفي آخر 24 ساعة بمقدار 5، مع بقاء الوصول العام مرتفعاً.
- حالة التحقق: غير موثّقة
- معدل التفاعل (ER): يبلغ متوسط تفاعل الجمهور 12.57%. وخلال أول 24 ساعة من النشر يحصد المحتوى عادةً 6.05% من ردود الفعل نسبةً إلى إجمالي المشتركين.
- وصول المنشورات: يحصل كل منشور على متوسط 2 222 مشاهدة. وخلال اليوم الأول يجمع عادةً 1 069 مشاهدة.
- التفاعلات والاستجابة: يتفاعل الجمهور بانتظام؛ متوسط التفاعلات لكل منشور يبلغ 23.
- الاهتمامات الموضوعية: يركز المحتوى على مواضيع رئيسية مثل sql, строка, users, индекс, user_id.
📝 الوصف وسياسة المحتوى
يصف المؤلف القناة بأنها مساحة للتعبير عن الآراء الذاتية:
“Авторский канал про Базы Данных и SQL
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!
Cотрудничество: @energy_c
РКН: https://clck.ru/3QREBc”
بفضل وتيرة التحديث المرتفعة (أحدث البيانات بتاريخ 19 أكتوبر, 2026) تحافظ القناة على حداثتها ومستوى وصول مرتفع. وتُظهر التحليلات تفاعلاً نشطاً من الجمهور، ما يجعلها نقطة تأثير مهمة ضمن فئة التكنولوجيات والتطبيقات.
SYSDATE возвращает текущие дату и время с точностью до секунды. Частая ошибка — пытаться выбрать записи «за сегодня» через прямое сравнение.
Представим таблицу:
orders(id, created_at DATE)
Интуитивный вариант:
SELECT *
FROM orders
WHERE created_at = SYSDATE;
Логически кажется правильным — взять записи за текущий день. Но условие почти никогда не выполнится.
Тип DATE в Oracle хранит и дату, и время (до секунды). Чтобы совпадение произошло, created_at должен быть ровно равен текущему моменту до секунды. На практике такой запрос почти всегда возвращает пустой результат.
Попытка исправить через TRUNC:
SELECT *
FROM orders
WHERE TRUNC(created_at) = TRUNC(SYSDATE);
Работает корректно, но есть нюанс — функция применяется к колонке, поэтому обычный индекс по created_at не используется (если только нет function-based index на TRUNC(created_at)).
Правильный способ — диапазон по дате:
SELECT *
FROM orders
WHERE created_at >= TRUNC(SYSDATE)
AND created_at < TRUNC(SYSDATE) + 1;
Такое условие индекс-дружелюбное (sargable), и оптимизатор может использовать индекс. Запрос выбирает все записи за текущие сутки независимо от времени (по часовому поясу сессии/сервера БД).
Аналогично можно искать за любой день:
WHERE created_at >= DATE '2026-02-18'
AND created_at < DATE '2026-02-19'
Поскольку DATE хранит время, прямое равенство по дате почти никогда не подходит для выборки «за день».
🔥 Для выборки за день используйте полуоткрытый диапазон, а не сравнение с SYSDATE.
➡️ SQL Ready | #практикаОставляю ссылочку: GitHub 📱➡️ SQL Ready | #репозиторий
• Определим, насколько далеко тянется текущая занятость, отслеживая максимальный конец интервалов; • Найдём точки разрыва, где начинается новый независимый блок времени; • Объединим пересечения в итоговые непрерывные диапазоны.Этот приём используют для анализа сессий, расписаний, бронирований и любых временных событий, где важно видеть реальные интервалы. ➡️ SQL Ready | #задача
SELECT отвечает за выборку данных, FROM — за указание источника (таблицы), а WHERE позволяет отфильтровать строки по условию.
На картинке — основные операторы SQL, типы JOIN’ов и правильный порядок выполнения запроса внутри СУБД.
Сохрани, чтобы не забыть!
➡️ SQL Ready | #ресурсnow() - created_at
Если нужно получить возраст записи в читаемом виде (годы-месяцы-дни), есть функция age(), которая учитывает календарь, а не просто секунды:
SELECT age(now(), created_at)
FROM orders;
А когда требуется число дней или часов для аналитики, интервал можно сразу привести к нужной единице:
EXTRACT(EPOCH FROM now() - created_at) / 3600
🔥 Полезно для TTL-логики, SLA-метрик, мониторинга задержек и любых задач, где время жизни записи, является ключевым параметром.
➡️ SQL Ready | #советDATE хранит дату и время одновременно. Частая задача — сравнить только по дате, игнорируя время. Многие используют TRUNC(date), но это может сильно ухудшить производительность.
Представим таблицу:
orders(id, created_at DATE)
Нужно выбрать заказы за 18 февраля 2026.
Интуитивный вариант:
SELECT *
FROM orders
WHERE TRUNC(created_at) = DATE '2026-02-18';
Логически верно, но есть проблема.
Функция применяется к колонке — поэтому обычный индекс по created_at в основном не используется, и Oracle вынужден вычислять TRUNC для множества строк, что часто приводит к полному сканированию.
Правильный способ — диапазон по дате:
SELECT *
FROM orders
WHERE created_at >= DATE '2026-02-18'
AND created_at < DATE '2026-02-19';
Теперь условие индекс-дружелюбное (sargable) — оптимизатор может использовать обычный индекс по created_at.
Этот подход корректно работает независимо от времени в поле.
TRUNC(date) полезен в SELECT или GROUP BY, когда фильтрация по индексу не критична:
SELECT
TRUNC(created_at) AS day,
COUNT(*) AS orders_count
FROM orders
GROUP BY TRUNC(created_at)
ORDER BY day;
Если всё же нужно часто фильтровать по TRUNC(created_at), можно создать функциональный индекс:
CREATE INDEX idx_orders_created_day
ON orders (TRUNC(created_at));
После этого запрос с TRUNC сможет использовать индекс.
Функция TRUNC умеет обрезать дату до разных уровней:
SELECT
TRUNC(SYSDATE) AS day,
TRUNC(SYSDATE, 'MM') AS month_start,
TRUNC(SYSDATE, 'YYYY') AS year_start
FROM dual;
Поддерживаются форматы: DD (день), MM (месяц), YYYY (год), IW (ISO-неделя) и др.
DATE хранит дату со временем, поэтому = DATE '2026-02-18' не находит все записи за день; функции над колонками мешают индексам.
🔥 Для фильтрации по дате используйте диапазоны, а TRUNC — для отображения, агрегации или вместе с функциональным индексом.
➡️ SQL Ready | #практика• Показано, как устроена учебная база, приближенная к реальным продакшен-сценариям с миллионами записей;
• Разобрано, как генерируются правдоподобные данные через имитацию работы системы, включая бронирования и жизненный цикл событий;
• Объясняется, какие задачи по SQL, аналитике и производительности можно отрабатывать на такой базе.
🔊 Продолжайте читать на Habr!➡️ SQL Ready | #статья
JOIN часто увеличивает результат или заставляет делать DISTINCT, что бьёт по производительности:
SELECT *
FROM orders o
WHERE EXISTS (
SELECT 1 FROM payments p
WHERE p.order_id = o.id
);
EXISTS останавливается на первой найденной строке и не создаёт дубликатов, поэтому обычно работает быстрее и предсказуемее:
SELECT *
FROM orders o
WHERE NOT EXISTS (
SELECT 1 FROM payments p
WHERE p.order_id = o.id
);
NOT EXISTS - наиболее корректный способ найти записи без соответствующих связанных данных.
В отличие от NOT IN он корректнее работает при наличии NULL:
SELECT *
FROM orders o
LEFT JOIN payments p ON p.order_id = o.id
WHERE p.order_id IS NULL;
🔥 LEFT JOIN + IS NULL делает то же самое логически, но чаще требует больше работы оптимизатора и может уступать по производительности на больших объёмах.
➡️ SQL Ready | #советorders(id, customer_id, amount, created_at)
Хотим увидеть все заказы и одновременно понимать, сколько денег каждый клиент потратил в сумме.
Первая мысль — посчитать через GROUP BY:
SELECT
customer_id,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;
Да, сумму он посчитает. Но список заказов пропадёт — останется по одной строке на клиента.
Если коротко: GROUP BY считает по группе, но детали внутри группы выбрасывает.
Решение через оконную функцию:
SELECT
id,
customer_id,
amount,
SUM(amount) OVER (PARTITION BY customer_id) AS total_amount
FROM orders;
PARTITION BY customer_id разбивает строки на независимые окна по клиентам.
SUM(...) OVER (...) считает сумму внутри каждого окна, но строки не схлопывает — каждая остаётся на месте.
В результате каждая строка — отдельный заказ и total_amount — сумма всех заказов этого клиента.
Фильтрация по агрегату без GROUP BY — например, нужно выбрать только заказы тех клиентов, у которых суммарный оборот больше 1000:
SELECT *
FROM (
SELECT
*,
SUM(amount) OVER (PARTITION BY customer_id) AS total_amount
FROM orders
) t
WHERE total_amount > 1000;
Обратите внимание: фильтрация происходит уже после вычисления оконной функции. Никаких GROUP BY, никаких дополнительных JOIN.
Та же логика через JOIN (для сравнения):
SELECT o.*
FROM orders o
JOIN (
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
) s ON s.customer_id = o.customer_id
WHERE s.total_amount > 1000;
Работает, но выглядит тяжелее: отдельный подзапрос, джойн, дублирование таблицы.
Можно посчитать сразу несколько агрегатов по тем же окнам:
SELECT
id,
customer_id,
amount,
SUM(amount) OVER (PARTITION BY customer_id) AS total_amount,
COUNT(*) OVER (PARTITION BY customer_id) AS orders_count
FROM orders;
Получаем и общую сумму, и количество заказов клиента — прямо в каждой строке.
🔥 Используйте оконные функции, когда нужно посчитать итог по группе, но не потерять сами строки. Это базовый приём аналитического SQL, который постоянно встречается в реальных задачах.
➡️ SQL Ready | #практика