🧠 Хитрая задача по SQL: максимум без агрегатов?
У тебя есть таблица
📌 Задача:
Для каждого клиента (`customer_id`) найти наиболее поздний заказ (по
🔥 Уловка:
💡 Подумай:
Как ты решишь эту задачу только с
📥 Ожидаемый результат:
```
🧩 Подсказка:
Можно использовать
📎 Такой приём полезен:
• Когда нельзя использовать оконные функции
• Когда ты работаешь на старых версиях СУБД
• Когда нужна универсальность между MySQL / Oracle / SQLite
#SQL #Задача #БазыДанных #DataEngineering #Оптимизация
@sqlhub
У тебя есть таблица
orders со следующими полями:
orders(id, customer_id, order_date, amount)
📌 Задача:
Для каждого клиента (`customer_id`) найти наиболее поздний заказ (по
order_date`), **не используя `GROUP BY и `MAX()`**.🔥 Уловка:
DISTINCT ON, TOP 1 WITH TIES и RANK() нельзя — ты ограничен базовым SQL, работающим на большинстве СУБД.💡 Подумай:
Как ты решишь эту задачу только с
JOIN, WHERE и EXISTS?📥 Ожидаемый результат:
```sql
customer_id | order_id | order_date | amount
------------|----------|------------|--------
1001 | 87 | 2024-12-01 | 320.00
1002 | 91 | 2024-12-05 | 175.00
...```
🧩 Подсказка:
Можно использовать
NOT EXISTS, чтобы выбрать заказы, у которых нет более новых у того же клиента.
SELECT o.*
FROM orders o
WHERE NOT EXISTS (
SELECT 1
FROM orders o2
WHERE o2.customer_id = o.customer_id
AND o2.order_date > o.order_date
)
📎 Такой приём полезен:
• Когда нельзя использовать оконные функции
• Когда ты работаешь на старых версиях СУБД
• Когда нужна универсальность между MySQL / Oracle / SQLite
#SQL #Задача #БазыДанных #DataEngineering #Оптимизация
@sqlhub
👍9❤5🔥4👎1
🧠 SQL-задача с подвохом: "Невидимые дубликаты"
В таблице
🎯 Цель:
Найти количество уникальных пользователей, если:
- Регистр не учитывается (`alice` = `ALICE`)
- Пробелы игнорируются
- Для
— Убираются точки в имени
— Всё после
✅ SQL-решение:
🔍 Как это работает:
LOWER(TRIM(email)) — убираем пробелы и регистр
SPLIT_PART(..., '+', 1) — отрезаем всё после +
REGEXP_REPLACE(..., '\.', '', 'g') — удаляем точки
Считаем DISTINCT, чтобы получить число уникальных email'ов
🔥 Используй такие трюки для:
• антифрода
• чистки базы
• аналитики поведения пользователей
#SQL #PostgreSQL #Gmail #EmailNormalization #DevTools #AntiFraud #DataCleaning #Analytics
В таблице
users хранятся email-адреса пользователей. Некоторые юзеры регистрируются повторно, маскируя один и тот же email по-разному:| id | name | email |
|----|----------|--------------------------|
| 1 | Alice | [email protected] |
| 2 | Bob | [email protected] |
| 3 | Charlie | [email protected] |
| 4 | Dave | [email protected] |
| 5 | Eve | [email protected] |
🎯 Цель:
Найти количество уникальных пользователей, если:
- Регистр не учитывается (`alice` = `ALICE`)
- Пробелы игнорируются
- Для
@gmail.com: — Убираются точки в имени
— Всё после
+ отрезается✅ SQL-решение:
SELECT COUNT(DISTINCT normalized_email) AS unique_users
FROM (
SELECT
CASE
WHEN email ILIKE '%@gmail.com' THEN
REGEXP_REPLACE(
SPLIT_PART(SPLIT_PART(LOWER(TRIM(email)), '+', 1), '@', 1),
'\.', '', 'g'
) || '@gmail.com'
ELSE
LOWER(REPLACE(TRIM(email), ' ', ''))
END AS normalized_email
FROM users
) AS cleaned;
🔍 Как это работает:
LOWER(TRIM(email)) — убираем пробелы и регистр
SPLIT_PART(..., '+', 1) — отрезаем всё после +
REGEXP_REPLACE(..., '\.', '', 'g') — удаляем точки
Считаем DISTINCT, чтобы получить число уникальных email'ов
🔥 Используй такие трюки для:
• антифрода
• чистки базы
• аналитики поведения пользователей
#SQL #PostgreSQL #Gmail #EmailNormalization #DevTools #AntiFraud #DataCleaning #Analytics
👍10❤4
This media is not supported in your browser
VIEW IN TELEGRAM
Иногда нужно найти пары строк, которые почти совпадают — например, из-за опечатки в одной букве. Такой кейс часто встречается при поиске дублей в именах, email или товарах.
С помощью функции
levenshtein() из расширения pg_trgm в PostgreSQL, можно находить строки, отличающиеся ровно на 1 символ. Это удобно для очистки данных, поиска дублей и реализации "умного" поиска в интерфейсе.
-- Убедись, что pg_trgm расширение включено
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- Найдём строки из таблицы users, у которых name отличается на 1 символ
SELECT a.name AS name1, b.name AS name2
FROM users a
JOIN users b ON a.id < b.id
WHERE levenshtein(a.name, b.name) = 1;
-- Пример: найдёт пары вроде ('Anna', 'Anya') или ('John', 'Joan')
📌Больше видео
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥9❤4👎1🥰1
🗄️ Вышел первый стабильный релиз ветки MariaDB 12.0 — версия 12.0.2
MariaDB 12.0 относится к промежуточным (rolling) выпускам и пришла на смену ветке 11.8. Поддержка этой ветки продлится до выхода MariaDB 12.1.2.
Параллельно представлен релиз-кандидат MariaDB 12.1.1.
📌 Напомним:
MariaDB — форк MySQL, совместимый по API/CLI, но с дополнительными движками хранения и расширенными функциями. Развивается MariaDB Foundation с открытым процессом разработки.
MariaDB уже заменяет MySQL во многих Linux-дистрибутивах (RHEL, Fedora, Debian, Arch и др.) и используется в крупных проектах вроде Wikipedia и Google Cloud SQL.
✨ Главное в MariaDB 12.0:
- 🔐 Поддержка SSL-ключей с паролем (`ssl_passphrase` или ввод вручную при запуске).
- 👤 Команда SET SESSION AUTHORIZATION — выполнение под другим пользователем (аналог sudo в БД).
- 🗝️ Плагин file_key_management.so — поддержка SHA-2.
- 🔄 Weak cursor variables (`SYS_REFCURSOR`) для возврата курсора из процедур и функций + настройка max_open_cursors.
- 📅 TO_CHAR — режим FM (Fill Mode) без лишних пробелов.
- 🛠 mariadb-check / CHECK TABLE теперь работают с таблицами SEQUENCE.
- ⚡ Оптимизатор — поддержка MySQL-совместимых *hints*: QB_NAME, BKA, NO_BKA, MAX_EXECUTION_TIME и др.
- 🌍 GIS-функции: ST_Validate, ST_GeoHash, ST_IsValid и др.
- 🔔 Триггеры для нескольких событий в одном CREATE TRIGGER.
- 📝 Audit-плагин пишет в лог и сетевой порт подключения.
- 📂 mariadb — новая опция --script-dir для кастомного каталога скриптов.
- 🗑️ Удалены устаревшие переменные: big_tables, large_page_size, storage_engine.
https://github.com/MariaDB/server/releases/tag/mariadb-12.0.2
#MariaDB #Database #SQL #Opensource
@sqlhub
MariaDB 12.0 относится к промежуточным (rolling) выпускам и пришла на смену ветке 11.8. Поддержка этой ветки продлится до выхода MariaDB 12.1.2.
Параллельно представлен релиз-кандидат MariaDB 12.1.1.
📌 Напомним:
MariaDB — форк MySQL, совместимый по API/CLI, но с дополнительными движками хранения и расширенными функциями. Развивается MariaDB Foundation с открытым процессом разработки.
MariaDB уже заменяет MySQL во многих Linux-дистрибутивах (RHEL, Fedora, Debian, Arch и др.) и используется в крупных проектах вроде Wikipedia и Google Cloud SQL.
✨ Главное в MariaDB 12.0:
- 🔐 Поддержка SSL-ключей с паролем (`ssl_passphrase` или ввод вручную при запуске).
- 👤 Команда SET SESSION AUTHORIZATION — выполнение под другим пользователем (аналог sudo в БД).
- 🗝️ Плагин file_key_management.so — поддержка SHA-2.
- 🔄 Weak cursor variables (`SYS_REFCURSOR`) для возврата курсора из процедур и функций + настройка max_open_cursors.
- 📅 TO_CHAR — режим FM (Fill Mode) без лишних пробелов.
- 🛠 mariadb-check / CHECK TABLE теперь работают с таблицами SEQUENCE.
- ⚡ Оптимизатор — поддержка MySQL-совместимых *hints*: QB_NAME, BKA, NO_BKA, MAX_EXECUTION_TIME и др.
- 🌍 GIS-функции: ST_Validate, ST_GeoHash, ST_IsValid и др.
- 🔔 Триггеры для нескольких событий в одном CREATE TRIGGER.
- 📝 Audit-плагин пишет в лог и сетевой порт подключения.
- 📂 mariadb — новая опция --script-dir для кастомного каталога скриптов.
- 🗑️ Удалены устаревшие переменные: big_tables, large_page_size, storage_engine.
https://github.com/MariaDB/server/releases/tag/mariadb-12.0.2
#MariaDB #Database #SQL #Opensource
@sqlhub
❤9👍4🔥2
🟡🔵 Разбираемся с SQL JOIN и фильтрами в OUTER JOIN
Одна из самых частых ошибок при работе с SQL - путаница между условием в
Когда мы пишем
✨ Пример:
У нас есть две таблицы:
- Левая: фигура + число
- Правая: число + фигура
Мы делаем
1. Фильтр в ON
Если написать
2. Фильтр в WHERE
Если написать
⚡ Почему это нужно знать?
-
-
- В
📌 Вывод:
- Если нужно оставить все строки из левой таблицы и лишь добавить совпадения справа - фильтр ставим в
- Если хотим действительно отобрать только подходящие строки — фильтр в
Именно поэтому в сложных запросах всегда спрашивай себя: фильтр — это часть логики соединения или это окончательное ограничение?
#SQL #joins #databases
Одна из самых частых ошибок при работе с SQL - путаница между условием в
ON и фильтром в WHERE. На картинке это отлично показано.Когда мы пишем
LEFT OUTER JOIN, мы ожидаем, что слева попадут все строки. Но результат зависит от того, где именно мы накладываем фильтры.✨ Пример:
У нас есть две таблицы:
- Левая: фигура + число
- Правая: число + фигура
Мы делаем
LEFT OUTER JOIN. 1. Фильтр в ON
Если написать
ON right_table.number = 1, то соединение будет проверять условие именно во время джойна. Это значит: строки слева сохранятся, даже если справа нет совпадений — просто будут NULL.2. Фильтр в WHERE
Если написать
WHERE left_table.number = 1, то фильтрация произойдёт уже после объединения. В этом случае строки, не прошедшие условие, полностью исчезнут из результата.⚡ Почему это нужно знать?
-
ON управляет логикой соединения. -
WHERE убирает строки после соединения. - В
OUTER JOIN это принципиальная разница: при фильтре в ON мы сохраним «пустые» строки, при фильтре в WHERE они будут удалены. 📌 Вывод:
- Если нужно оставить все строки из левой таблицы и лишь добавить совпадения справа - фильтр ставим в
ON. - Если хотим действительно отобрать только подходящие строки — фильтр в
WHERE. Именно поэтому в сложных запросах всегда спрашивай себя: фильтр — это часть логики соединения или это окончательное ограничение?
#SQL #joins #databases
❤9👍9🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
🚨 SQL Никогда НЕ ДЕЛАЙ ТАК #sql
НИКОГДА НЕ ЛОМАЙ ИНДЕКСЫ ФУНКЦИЯМИ: не оборачивай индексируемые поля в функции внутри WHERE.
Как только ты пишешь LOWER(), CAST(), COALESCE() или любые вычисления по колонке — индекс перестаёт работать, и запрос падает в полное сканирование таблицы.
Это одна из самых тихих причин, почему запросы внезапно превращаются в тормоза.
Вместо этого приводи значения заранее или используй функциональные индексы.
НИКОГДА НЕ ЛОМАЙ ИНДЕКСЫ ФУНКЦИЯМИ: не оборачивай индексируемые поля в функции внутри WHERE.
Как только ты пишешь LOWER(), CAST(), COALESCE() или любые вычисления по колонке — индекс перестаёт работать, и запрос падает в полное сканирование таблицы.
Это одна из самых тихих причин, почему запросы внезапно превращаются в тормоза.
Вместо этого приводи значения заранее или используй функциональные индексы.
Плохо: индекс по email НЕ используется
SELECT *
FROM users
WHERE LOWER(email) = '[email protected]';
-- Хорошо: нормализуем значение заранее
SELECT *
FROM users
WHERE email = '[email protected]';
-- Или создаём функциональный индекс (PostgreSQL)
CREATE INDEX idx_users_email_lower ON users (LOWER(email));
👍22❤9🔥6
XiYan-SQL - это open-source решение, позволяющее генерировать, анализировать и выполнять SQL-запросы с использованием больших языковых моделей. Инструмент ориентирован на ускорение исследования данных и автоматизацию рутинных операций, связанных с запросами к базе.
Ключевые возможности:
- Генерация SQL из естественного языка -пользователь формулирует задачу обычными словами, а система преобразует её в корректный SQL-запрос.
- Интерактивная работа с базой данных - запросы можно оперативно уточнять, редактировать и выполнять, получая быстрый цикл обратной связи.
- Поддержка нескольких СУБД - PostgreSQL, MySQL, SQLite и другие.
- 🛠️ Минимальная конфигурация - подходит для анализа данных, прототипирования и облегчения доступа к базе без сложной инфраструктуры.
Преимущества использования:
- Существенно снижает трудоёмкость написания сложных SQL-запросов.
- Упрощает работу аналитикам и разработчикам, которым важно быстро получать корректные результаты.
- Может выступать в роли интерактивного помощника для изучения структуры базы и построения отчётов.
🔗 Репозиторий: github.com/XGenerationLab/XiYan-SQL
@ai_machinelearning_big_data
#sql #llm #ai #opensource #database #datatools #postgresql
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤7👎6🥰1
⚡️ SQL-прием: EXISTS часто лучше, чем COUNT(*) > 0
Если тебе нужно просто проверить, есть ли строки, не заставляй базу считать их все.
Плохо:
База может пройти по всем подходящим строкам, чтобы посчитать количество.
Лучше:
EXISTS останавливается сразу, как только нашел первую подходящую строку. Для больших таблиц это может быть заметно быстрее, особенно если есть индекс по условию:
Если тебе нужен ответ “есть или нет”, используй EXISTS. COUNT(*) оставь для случаев, когда реально нужно точное количество строк.
#sql #postgresql #database #backend
Если тебе нужно просто проверить, есть ли строки, не заставляй базу считать их все.
Плохо:
SELECT COUNT(*) > 0
FROM orders
WHERE user_id = 42;
База может пройти по всем подходящим строкам, чтобы посчитать количество.
Лучше:
SELECT EXISTS (
SELECT 1
FROM orders
WHERE user_id = 42
);
EXISTS останавливается сразу, как только нашел первую подходящую строку. Для больших таблиц это может быть заметно быстрее, особенно если есть индекс по условию:
CREATE INDEX idx_orders_user_id ON orders(user_id);
Если тебе нужен ответ “есть или нет”, используй EXISTS. COUNT(*) оставь для случаев, когда реально нужно точное количество строк.
#sql #postgresql #database #backend
👍11❤8🔥3
This media is not supported in your browser
VIEW IN TELEGRAM
🚀 PgQue – Устойчивые очереди в Postgres
PgQue предлагает универсальную архитектуру очередей для PostgreSQL, основанную на проверенной модели PgQ. Это решение без лишних зависимостей, работающее на любом управляемом Postgres, обеспечивая нулевое бремя и стабильную производительность под нагрузкой.
🚀 Основные моменты:
- Никаких внешних демонов или расширений
- Использует SQL и PL/pgSQL для установки
- Обеспечивает ACID-транзакции и долговечность
- Никакого накопления "мертвых" кортежей
- Подходит для высоконагруженных систем
📌 GitHub: https://github.com/NikolayS/pgque
#sql
PgQue предлагает универсальную архитектуру очередей для PostgreSQL, основанную на проверенной модели PgQ. Это решение без лишних зависимостей, работающее на любом управляемом Postgres, обеспечивая нулевое бремя и стабильную производительность под нагрузкой.
🚀 Основные моменты:
- Никаких внешних демонов или расширений
- Использует SQL и PL/pgSQL для установки
- Обеспечивает ACID-транзакции и долговечность
- Никакого накопления "мертвых" кортежей
- Подходит для высоконагруженных систем
📌 GitHub: https://github.com/NikolayS/pgque
#sql
❤2👍1🔥1
Хитрый SQL-совет: осторожнее с `NOT IN`
Кажется, что эти запросы делают одно и то же:
Но если banned_users.user_id содержит хотя бы один NULL, запрос может вернуть ноль строк.
Надёжнее использовать NOT EXISTS:
Причина в трёхзначной логике SQL: сравнение с NULL даёт UNKNOWN, а не TRUE или FALSE.
Правило простое: если подзапрос потенциально возвращает NULL, вместо NOT IN почти всегда выбирайте NOT EXISTS.
#sql #postgresql #database
Кажется, что эти запросы делают одно и то же:
SELECT *
FROM users
WHERE id NOT IN (
SELECT user_id
FROM banned_users
);
Но если banned_users.user_id содержит хотя бы один NULL, запрос может вернуть ноль строк.
Надёжнее использовать NOT EXISTS:
SELECT u.*
FROM users AS u
WHERE NOT EXISTS (
SELECT 1
FROM banned_users AS b
WHERE b.user_id = u.id
);
Причина в трёхзначной логике SQL: сравнение с NULL даёт UNKNOWN, а не TRUE или FALSE.
Правило простое: если подзапрос потенциально возвращает NULL, вместо NOT IN почти всегда выбирайте NOT EXISTS.
#sql #postgresql #database
🔥18👍7❤1🥰1
💡 SQL-трюк: сравнивайте `NULL` без костылей
В PostgreSQL обычное сравнение может неожиданно сломать условие:
Результат:
Потому что
Из-за этого часто пишут громоздкие условия:
Но есть оператор, о котором многие забывают:
Он работает как NULL-safe equality:
Есть и обратный вариант:
Например, удобно искать реально изменившиеся значения:
Если оба
Без этого обычное:
может просто вернуть
Особенно полезно при синхронизации данных, аудите изменений, ETL и UPSERT-логике.
#SQL #PostgreSQL #Database
В PostgreSQL обычное сравнение может неожиданно сломать условие:
SELECT NULL = NULL;
Результат:
NULL
Потому что
NULL означает «неизвестное значение», а не конкретное значение.Из-за этого часто пишут громоздкие условия:
WHERE a = b
OR (a IS NULL AND b IS NULL)
Но есть оператор, о котором многие забывают:
a IS NOT DISTINCT FROM b
Он работает как NULL-safe equality:
SELECT NULL IS NOT DISTINCT FROM NULL; -- true
SELECT 10 IS NOT DISTINCT FROM 10; -- true
SELECT 10 IS NOT DISTINCT FROM NULL; -- false
Есть и обратный вариант:
a IS DISTINCT FROM b
Например, удобно искать реально изменившиеся значения:
SELECT *
FROM old_data o
JOIN new_data n USING (id)
WHERE o.email IS DISTINCT FROM n.email;
Если оба
email = NULL, строка не считается изменённой.Без этого обычное:
o.email <> n.email
может просто вернуть
NULL и пропустить изменение.Особенно полезно при синхронизации данных, аудите изменений, ETL и UPSERT-логике.
#SQL #PostgreSQL #Database
🔥9👍8❤7
⚡️ SQL-приём: `GROUPING SETS` может заменить несколько тяжёлых `GROUP BY` + `UNION ALL`.
Допустим, нужно одновременно получить статистику:
- по стране и городу;
- только по стране;
- общий итог.
Часто пишут так:
Но SQL умеет это нативно:
() означает grand total.
А если нужно понять, настоящий ли NULL лежит в данных или это строка итогов:
вернут 1 для колонок, которые были свернуты агрегированием.
🔥 Особенно полезно для:
OLAP-запросов;
аналитических отчётов;
дашбордов;
многоуровневых итогов;
запросов, где иначе появляется несколько почти одинаковых GROUP BY.
Ещё есть:
ROLLUP строит иерархические итоги, а CUBE - все комбинации измерений.
Если в аналитическом SQL у вас появляется цепочка из GROUP BY + UNION ALL, возможно, вы просто забыли про GROUPING SETS.
#SQL #PostgreSQL #DataEngineering #Analytics
Допустим, нужно одновременно получить статистику:
- по стране и городу;
- только по стране;
- общий итог.
Часто пишут так:
SELECT country, city, SUM(revenue)
FROM sales
GROUP BY country, city
UNION ALL
SELECT country, NULL, SUM(revenue)
FROM sales
GROUP BY country
UNION ALL
SELECT NULL, NULL, SUM(revenue)
FROM sales;
Но SQL умеет это нативно:
SELECT
country,
city,
SUM(revenue) AS revenue
FROM sales
GROUP BY GROUPING SETS (
(country, city),
(country),
()
);
() означает grand total.
А если нужно понять, настоящий ли NULL лежит в данных или это строка итогов:
GROUPING(country)
GROUPING(city)
вернут 1 для колонок, которые были свернуты агрегированием.
🔥 Особенно полезно для:
OLAP-запросов;
аналитических отчётов;
дашбордов;
многоуровневых итогов;
запросов, где иначе появляется несколько почти одинаковых GROUP BY.
Ещё есть:
ROLLUP(...)
CUBE(...)
ROLLUP строит иерархические итоги, а CUBE - все комбинации измерений.
Если в аналитическом SQL у вас появляется цепочка из GROUP BY + UNION ALL, возможно, вы просто забыли про GROUPING SETS.
#SQL #PostgreSQL #DataEngineering #Analytics
❤9👍6🔥6🤔1😱1