Telegram Web
🖥 Задача: “Невидимая строка”

Дана таблица users:

CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT,
deleted_at TIMESTAMP
);


Вы выполняете:

SELECT COUNT(*) FROM users WHERE deleted_at = NULL;


✔️ И получаете... 0. Хотя вы уверены, что есть пользователи, у которых deleted_at не задано.

Вопрос:
Что не так с запросом? Как правильно получить количество “неудалённых” пользователей?

Вопрос: В чем ошибка? И как надо правильно писать такой фильтр?

✔️ Правильный запрос будет выглядеть так:

Чтобы проверить, является ли значение NULL, нужно использовать специальный оператор IS NULL.

SELECT COUNT(*) FROM users WHERE deleted_at IS NULL;

Этот запрос корректно найдёт все строки, где в столбце deleted_at действительно отсутствует значение (т.е. он равен NULL), и посчитает их количество.

Итог:
Не используйте = или != для сравнения с NULL.
Используйте IS NULL для проверки на NULL.
Используйте IS NOT NULL для проверки на не NULL (если бы вам понадобилось посчитать удалённых пользователей, у которых deleted_at заполнено).


@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍263🔥3
✔️ Presto (prestoDB/presto)

Это высокопроизводительный распределённый движок SQL для анализа больших объёмов данных в реальном времени. Основные характеристики:

Распределённая архитектура

Состоит из координатора и множества воркеров, которые параллельно выполняют фрагменты запросов, обеспечивая низкую задержку даже при обработке петабайтовых данных
GitHub

Поддержка ANSI SQL и UDF

Полноценная поддержка стандартного SQL с возможностью расширения набором пользовательских функций (UDF), агрегатов и аналитических функций.

Плагинные коннекторы
Из коробки доступны коннекторы к Hive (HDFS/S3), Cassandra, Kafka, MongoDB, MySQL, PostgreSQL, Elasticsearch и многим другим системам хранения. При желании можно написать собственный плагин
GitHub
.

Масштабируемость и отказоустойчивость
Горизонтальное масштабирование за счёт добавления воркеров, автоматическое перераспределение задач при выходе узлов из строя.

Удобство развёртывания
Можно собрать из исходников через Maven (./mvnw clean install), развернуть через Docker (официальный образ есть в папке docker/), или сразу использовать готовые пакеты на prestodb.io.

Клиенты и интеграции
Имеется CLI (presto-cli), JDBC/ODBC‑драйверы, REST API. Лёгко встраивается в BI‑инструменты и платформы визуализации.

Сферы применения
Ad‑hoc‑аналитика, интерактивные дашборды, federated query (объединение данных из разных источников), подготовка данных для машинного обучения.

https://github.com/prestodb/presto

@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Нашел лучший сайт для изучения SQL
Хороший ресурс для освоения SQL — SQL Academy!

Это интерактивная платформа с практическими заданиями от ведущих российских компаний: ВКонтакте, Альфа-Банка, Сбера и других. Здесь найдётся всё, что нужно разработчикам, аналитикам, тестировщикам и студентам, интересующимся базами данных.интересующихся студентов.

Попробовать здесь

@sqlhub
9👍5👎1🔥1
✔️ Wal-listener — это инструмент для прослушивания логов транзакций PostgreSQL (WAL) и конвертации их в удобный для обработки формат JSON.

Возможности

- Прослушивание изменений в PostgreSQL в режиме реального времени.
- Поддержка нескольких слотов репликации.
- Удобный вывод в формате JSON.
- Готов к использованию в качестве сервиса.

Пример использования

1. Создаём слот репликации:

SELECT * FROM pg_create_logical_replication_slot('test_slot', 'wal2json');


2. Запускаем wal-listener:

wal-listener --dsn "host=localhost port=5432 user=postgres dbname=test" --slot test_slot


3. Получаем JSON-объекты при изменениях в базе данных.

https://github.com/ihippik/wal-listener

#devops #девопс #PostgreSQL #sql

@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍52
⚡️Легкий способ получать свежие обновления и следить за трендами в разработке на вашем языке. Находите свой стек и подписывайтесь:

Python: www.tgoop.com/pythonl
Linux: www.tgoop.com/linuxacademiya
Собеседования DS: www.tgoop.com/machinelearning_interview
Нерйросети www.tgoop.com/ai_machinelearning_big_data
C++ www.tgoop.com/cpluspluc
Docker: www.tgoop.com/DevopsDocker
Хакинг: www.tgoop.com/linuxkalii
Devops: www.tgoop.com/DevOPSitsec
Data Science: www.tgoop.com/data_analysis_ml
Javascript: www.tgoop.com/javascriptv
C#: www.tgoop.com/csharp_ci
Java: www.tgoop.com/javatg
Базы данных: www.tgoop.com/sqlhub
Python собеседования: www.tgoop.com/python_job_interview
Мобильная разработка: www.tgoop.com/mobdevelop
Golang: www.tgoop.com/Golang_google
React: www.tgoop.com/react_tg
Rust: www.tgoop.com/rust_code
ИИ: www.tgoop.com/vistehno
PHP: www.tgoop.com/phpshka
Android: www.tgoop.com/android_its
Frontend: www.tgoop.com/front
Big Data: www.tgoop.com/bigdatai
МАТЕМАТИКА: www.tgoop.com/data_math
Kubernets: www.tgoop.com/kubernetc
Разработка игр: https://www.tgoop.com/gamedev
Haskell: www.tgoop.com/haskell_tg
Физика: www.tgoop.com/fizmat

💼 Папка с вакансиями: www.tgoop.com/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: www.tgoop.com/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: www.tgoop.com/addlist/eEPya-HF6mkxMGIy
Папка ML: https://www.tgoop.com/addlist/2Ls-snqEeytkMDgy
Папка FRONTEND: https://www.tgoop.com/addlist/mzMMG3RPZhY2M2Iy

😆ИТ-Мемы: www.tgoop.com/memes_prog
🇬🇧Английский: www.tgoop.com/english_forprogrammers
🧠ИИ: www.tgoop.com/vistehno

🎓954ГБ ОПЕНСОРС КУРСОВ: @courses
📕Ит-книги бесплатно: https://www.tgoop.com/addlist/BkskQciUW_FhNjEy
👍53🔥3
🖥 Задача: Анализ пользовательского поведения с аномалиями в SQL

## Условие задачи:

Дана таблица user_events со следующей структурой:


CREATE TABLE user_events (
user_id INT,
event_time TIMESTAMP,
event_type VARCHAR(50),
platform VARCHAR(50)
);


🎯 Каждая строка описывает событие пользователя:
- user_id — идентификатор пользователя,
- event_time — время события,
- event_type — тип события (`login`, purchase, logout, error и т.д.),
- platform — платформа (`iOS`, Android, `Web`).

Требуется:

1. Найти пользователей, которые:
- Выполнили покупку (`purchase`),
- Но не заходили в систему (`login`) в течение последних 7 дней перед покупкой.

2. Найти пользователей, у которых:
- Более 30% всех событий за последний месяц составляют события типа error.

3. Рассчитать для каждого пользователя:
- Среднее время между входом (`login`) и следующим выходом (`logout`).
- Если logout отсутствует после login — игнорировать такую сессию.

---

## Дополнительные условия:

- Считайте, что данные могут быть объемными: миллионы строк.
- Решение должно быть оптимизировано: избегайте подзапросов в подзапросах без индексов, старайтесь минимизировать количество проходов по данным.
- Можно использовать оконные функции (`WINDOW FUNCTIONS`) и временные таблицы (`CTE`) для упрощения запросов.
- Платформу можно игнорировать в расчетах.

---

## Что оценивается:

- Умение использовать оконные функции и агрегаты.
- Умение правильно интерпретировать условия задачи в SQL-операции.
- Оптимизация запросов под большие объемы данных.
- Чистота, читаемость и структурированность кода SQL-запросов.

---

Примечание:
Эта задача проверяет как технические навыки работы с SQL, так и внимательность к деталям формулировки задачи. Небрежная реализация может дать неверные результаты, особенно на больших данных.

🔥 Подсказки и намёки для решения задачи


## Задание 1: Найти пользователей с покупками без логина за последние 7 дней

**Намёк:**
- Используйте оконную функцию LAG() или MAX() с фильтрацией событий login.
- Для каждой покупки проверяйте, был ли login в пределах 7 дней до события purchase.
- Можно применить LEFT JOIN событий login к событиям purchase.

## Задание 2: Найти пользователей с долей ошибок > 30%

**Намёк:**
- Используйте оконные функции COUNT(*) и SUM(CASE WHEN event_type = 'error' THEN 1 ELSE 0 END).
- Постройте долю ошибок на основе всех событий пользователя за последние 30 дней (`WHERE event_time >= CURRENT_DATE - INTERVAL '30 days'`).

## Задание 3: Рассчитать среднее время между login и следующим logout

**Намёк:**
- Используйте оконную функцию LEAD() для поиска следующего события после login.
- Пара login -> logout должна иметь корректный порядок по времени.
- Отбрасывайте случаи, где следующего logout нет или это событие другого типа.

@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
12👍11🔥4
🔍 Milvus — масштабируемая высокопроизводительная векторная БД для AI-приложений с нативной интеграцией с Kubernetes.

Проект написан на Go и C++ и оптимизирован для работы с миллиардами векторов в реальном времени. Помимо классических dense-векторов, Milvus поддерживает sparse-модели для полнотекстового поиска и гибридные запросы. Для локального тестирования есть облегченная версия, устанавливаемая через pip.

🤖 GitHub

@sqlhub
👍74🔥2
SQL Basics.pdf
102.8 KB
🖥 Крутая подборка шпаргалок по SQL

Внутри

— SQL для data analysis;
— SQL Joins;
— Оконные функции;
— Основы SQL.

@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🔥85
Media is too big
VIEW IN TELEGRAM
NotebookLM теперь умеет делать подкасты на русском и других славянских языках

Новая функция позволяет превращать документы в аудио — можно генерировать как полезные, так и совсем абсурдные подкасты (пример в посте). Идеально, чтобы слушать вместо чтения, например, во время пробежки.

Озвучка на русском немного уступает английской, но звучит достойно.

🎧 Бесплатно, пробуем
🔥113👍3
✔️ А вот и новый DeepSeek Prover v2, модель, заточенная исключительно на математику.

🚀Масштабная архитектура на базе, которая содержит 671 млрд параметров, что в 96 раз больше, чем у предыдущей версии Prover-V1.5 (7 млрд).

Построен на базе архитектуры «смеси экспертов» (MoE), что снижает затраты на обучение и повышает эффективность решения задач.

Модель заточена на формальное доказательство теорем с помощью языка программирования Lean 4, обеспечивая 100% логическую точность.

Lean 4 — это зависимо типизированный функциональный язык программирования и интерактивное средство доказательства теорем.

Результаты:
Новая Sota( 88,9%) на MiniF2F-test.
• DeepSeek-Prover-V2 смогла доказать 49 теорем из 658.

Для тренировки использовались 8 млн синтетических примеров, созданных через рекурсивный поиск решений теорем.

🔍 Как это работает:

1) Разложение теорем: DeepSeek-V3 по prompt'у разбивает сложные задачи на подцели.

2) Формализация: Пошаговые рассуждения переводятся в доказательства на Lean 4.

3) Cold-start: Полученные цепочки рассуждений и формальные доказательства используются как начальные данные для обучения модели.

🌟 Два размера:
7 B — базовый вариант.
671 B — расширенная версия на базе DeepSeek-V3-Base.

https://huggingface.co/deepseek-ai/DeepSeek-Prover-V2-671B
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥42👎1
👣 Небольшой пример как копировать данные между базами данных используя `go`, `pgx`, и `copy`

Предположим что у нас есть два коннекта к базе (одной или нескольким, это не важно). Далее используя io.Pipe() создаём Reader и Writer, и используя CopyTo() и CopyFrom() переносим данные.

r, w := io.Pipe()

doneChan := make(chan struct{}, 1)

go func() {
defer close(doneChan)

_, err := db1.PgConn().CopyTo(ctx, w, `copy table1 to stdin binary`)
if err != nil {
slog.Error("error", "error", err)
return
}
_ = w.Close()
doneChan <- struct{}{}
}()

_, err = db2.PgConn().CopyFrom(ctx, r, `copy table1 from stdout binary`)
_ = r.Close()

select {
case <-doneChan:
case <-ctx.Done():
}


Вся прелесть тут в том что используем наиболее быстрый способ с точки зрения PostgreSQL.

Используя `copy (select * from where ... order by ... limit ...) to stdout `можем регулировать нагрузку на чтение, следить за прогрессом и управлять копированием данных.

В качестве Reader может выступать что угодно, хоть файл csv, хоть другая СУБД, но тогда данные придётся дополнительно конвертировать в формат понимаемый PostgreSQL - csv или tsv, и использовать copy ... from stdin (format csv).

Нюанс: copy ... from stdin binary , binary обязывает использовать одинаковые типы данных, нельзя будет integer колонку перенести в колонку smallint, если такое требуется, то параметр binary надо опустить.

Весь код тут. И ещё немного кода для вдохновения.

@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍98🔥5😁1
## 7 полезных приёмов для Oracle SQL

Простые советы для Oracle SQL, которые помогут аналитикам данных прокачать свои запросы.

1) Фильтрованные (partial) индексы
В Oracle можно создавать индексы только для подмножества строк, чтобы ускорить выборку по популярным условиям.

CREATE INDEX idx_orders_high_value
ON orders(order_date)
WHERE total_amount > 1000;


2) Функциональные (function-based) индексы
Если фильтруете или джойните по функции, создайте индекс прямо по выражению:

CREATE INDEX idx_orders_year
ON orders (EXTRACT(YEAR FROM order_date));


3) GROUPING SETS, ROLLUP, CUBE
Для одновременной агрегации по нескольким группировкам без UNION ALL:

SELECT region, category, SUM(sales) AS total
FROM sales
GROUP BY ROLLUP (region, category);


4) Материализованные представления с QUERY REWRITE
В Oracle можно сделать автоматическую подмену сложного запроса предрасчитанным результатом (материализованным представлением):

CREATE MATERIALIZED VIEW mv_sales_by_month
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
ENABLE QUERY REWRITE
AS
SELECT TRUNC(order_date, 'MM') AS month, SUM(total_amount) AS total
FROM orders
GROUP BY TRUNC(order_date, 'MM');

Теперь запрос SELECT month, SUM(total_amount) FROM orders GROUP BY month; автоматически будет использовать mv_sales_by_month.

5) WITH PL/SQL FUNCTION RESULT CACHE
Кэшируйте результат функции, чтобы при одинаковых входных данных не пересчитывать:

CREATE OR REPLACE FUNCTION get_tax_rate(p_region VARCHAR2)
RETURN NUMBER RESULT_CACHE RELIES_ON (tax_table) IS
v_rate NUMBER;
BEGIN
SELECT rate INTO v_rate FROM tax_table WHERE region = p_region;
RETURN v_rate;
END;


6) PARALLEL HINT для ускорения запросов
Явно указывайте параллельное выполнение запроса, чтобы задействовать несколько процессов:

SELECT /*+ PARALLEL(orders, 4) */ customer_id, SUM(total_amount)
FROM orders
GROUP BY customer_id;


7) DBMS_STATS.AUTO_SAMPLE_SIZE для сбора статистики
Используйте автоматический подбор размера выборки для более точной оптимизации плана выполнения:

EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'ORDERS', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE);

Совет: проверяйте планы выполнения через DBMS_XPLAN.DISPLAY_CURSOR, чтобы видеть реальные шаги запроса, а не только предполагаемые.

@sqlhub
👍105🔥4👎1
Media is too big
VIEW IN TELEGRAM
📜 История SQL — от лабораторной идеи до «языка данных» № 1

Как появился самый известный язык работы с базами, почему он едва не остался «Сиквелом» и какие любопытные факты о нём редко всплывают в учебниках.

1. Всё началось с таблицы на бумаге

- 1970 г. — британский математик Эдгар Ф. Кодд публикует культовую статью *“A Relational Model of Data for Large Shared Data Banks”*.
- В ней впервые прозвучала идея: хранить данные в виде связанных таблиц, а не как запутанные иерархии (IMS) или сетевые графы (Codasyl).
- Коллеги в IBM скептически называли это «бумагой на буквы», но разрешили сделать прототип, чтобы проверить утопию Кодда на практике.

2. SEQUEL — «английский» запрос к таблицам

- 1973–1974 гг. — в лаборатории IBM San José (ныне Almaden) двое молодых исследователей, Дональд Чемберлин и Рэймонд Бойс, берутся за проект System R.
- Чтобы обращаться к реляционным таблицам, они придумывают Structured English QUEry Language — SEQUEL.
- Ключевая фишка — запросы выглядят почти как английские предложения:

SELECT name, salary
FROM employees
WHERE dept = 'R&D';

- В 1974‑м публикуют первую спецификацию; академики критикуют за «слишком поверхностный английский», но программисты в восторге.

3. Почему SEQUEL стал SQL

- Торговая марка “SEQUEL” уже принадлежала авиастроительной компании *Hawker Siddeley*.
- IBM, опасаясь суда, в 1976 г. официально отказывается от «E» и оставляет SQL (Structured Query Language).
- *Небольшая путаница осталась навсегда: кто‑то произносит «эс‑кью‑эл», кто‑то — «сиквел».*

4. Коммерческий взлёт


- 1978 | Первая демонстрация System R внутри IBM | показала, что SQL работает быстрее ожиданий |
- 1979 | Стартап Relational Software (позже Oracle**) выпускает **Oracle V2 — первый коммерческий SQL‑движок | IBM ещё не успела выйти на рынок
- 1981 | IBM выпускает SQL/DS для мейнфреймов | стандарт де‑факто закрепляется
- 1983 | Дебют DB2 — теперь SQL есть почти в каждом крупном банке

5. Стандартизация и эволюция

- ANSI SQL‑86SQL‑92 (появился `JOIN ... ON`) → SQL:1999 (рекурсия, триггеры) → SQL:2003 (XML) → … → SQL:2023 (JSON, property graphs).
- Каждые 3–5 лет комитет добавляет «модные» возможности, но 90 % повседневных запросов всё ещё укладываются в синтаксис 1980‑х.

6. Забавные факты, которые украсят small talk 🍸

1. NULL ≠ 0 и NULL ≠ NULL — «неизвестное значение» нарушает законы логики, за что его зовут *“пятой ногой”* реляционной алгебры.
2. `SELECT *` — наследие печати на станке. Звёздочка означала «все колонки», чтобы не писать их руками в 132‑символьных перфокартах.
3. Команда GO в MS SQL Server не принадлежит стандарту SQL — это директива из старого клиента isql.
4. В Oracle долго не было LIMIT, а в MySQL —QL — от лабора Поэтому админы шутили: «истинный межплатформенный SQL — это `SELECT 1;`».
5. Первый SQL‑вирус — червь *Slammer* (2003) — парализовал интернет за 10 минут через уязвимость в SQL Server 2000.
6. SQL — декларативный язык, но внутри СУБД каждый SELECT превращается в процедурный план.
7.Ф. Кодд публикуетпридумали позже, чемлабораторн Сначала удалять целую БД казалось слишком опасным.

7. Почему SQL живёт дольше модных NoSQL‑наследников

- Математическая база. Таблицы + операции Кодда образуют алгебру с предсказуемой оптимизацией.
- Стандарты и переносимость. Код двадцатилетней давности можно запустить в современной Postgres или MariaDB.
- Большая экосистема. От Excel‑плагинов до BigQuery — везде так или иначе поддерживается SQL‑диалект.
- Сопротивляемость моде. Каждый «убийца SQL» (MapReduce, GraphQL, документные БД) в итоге добавляет свой адаптер SELECT ….

Итог: SQL родился как эксперимент IBM, пережил смену названий и юридические баталии, но в итоге стал «лентой Мёбиуса» мира данных: можно зайти с любой стороны — и всё равно окажешься в FROM.

https://www.youtube.com/shorts/EuFjzuVHkHE

@sqlhub -подписаться
👍28🔥10🥰41
🧩 Задача для SQL-аналитиков: "Пропавшие продажи"

📖 Описание задачи

У вас есть таблица sales, где хранятся данные о продажах:


CREATE TABLE sales (
sale_id INT PRIMARY KEY,
sale_date DATE,
product_id INT,
quantity INT,
price DECIMAL(10,2)
);

INSERT INTO sales (sale_id, sale_date, product_id, quantity, price) VALUES
(1, '2024-01-01', 101, 1, 100.00),
(2, '2024-01-02', 102, 2, 150.00),
-- ...
-- остальные данные
;


Каждый день формируется отчёт, где суммируются продажи по дням:


SELECT sale_date, SUM(quantity * price) AS total_sales
FROM sales
GROUP BY sale_date;


Вчера сумма в отчёте была 1,000,000. Сегодня — 980,000, хотя новых записей не удаляли.

📝 Ваша задача:

1. Найти, какие записи "исчезли" из отчёта, если данных в таблице sales фактически не удаляли.
2. Определить, почему эти записи больше не попадают в итоговый запрос.
3. Исправить отчёт, чтобы сумма снова стала 1,000,000.

Ограничения:

- Таблица не изменилась по количеству строк.
- Никто не менял код запроса.
- sale_date, quantity, price остались без изменений.

Подсказка: возможно, дело в NULL, JOIN или неправильной агрегации.

🕵️ Что проверяет задача:

- Знание SQL-агрегации
- Понимание NULL и работы SUM
- Умение анализировать запросы «не через код», а через их результат
- Навык находить «скрытые» ошибки данных (например, sale_date стал NULL)

💡 Решение:

При проверке выяснится, что часть записей имеет `sale_date = NULL` (например, кто-то обновил поле
sale_date на NULL).

Итоговый запрос:

```sql
SELECT sale_date, SUM(quantity * price) AS total_sales
FROM sales
GROUP BY sale_date;
```

не учитывает строки, где `sale_date IS NULL`, потому что
GROUP BY игнорирует NULL как отдельную группу (не попадает ни в один существующий `sale_date`).

Чтобы увидеть эти записи:

```sql
SELECT sale_date, COUNT(*), SUM(quantity * price)
FROM sales
GROUP BY sale_date;
```

Для восстановления суммы нужно добавить обработку NULL, например:

```sql
SELECT COALESCE(sale_date, 'unknown') AS sale_date, SUM(quantity * price) AS total_sales
FROM sales
GROUP BY COALESCE(sale_date, 'unknown');
```

Теперь сумма снова будет 1,000,000, а "пропавшие" продажи попадут в отдельную категорию
unknown.

🎯 Эта задача учит:

Всегда думать о данных, а не только о коде
Проверять поля на NULL даже там, где их не ожидаешь
Уметь объяснять ошибки «бизнес-заказчику», а не только исправлять запрос

🔥 Отличная тренировка внимательности и понимания нюансов SQL-агрегации!

@sqlhub
👍17👎54🔥3😁1
💫 GlueSQL — SQL-движок, превращающий любые данные в полноценную базу данных. Этот инструмент умеет выполнять JOIN между CSV и MongoDB, работать с Git как с хранилищем данных и даже запускать SQL-запросы прямо в браузере через WebAssembly.

Что отличает GlueSQL от классических СУБД?
- Поддержка schemaless-данных
- Встроенные адаптеры для 10+ форматов
- Возможность добавлять свои хранилища через реализацию двух traits на Rust

Проект активно развивается: недавно добавили поддержку транзакций в Sled-бэкенде и анонсировали облачную версию. Для теста достаточно cargo add gluesql и уже можно писать SQL-запросы к данным в памяти.

🤖 GitHub

@sqlhub
👍9🔥62
🖥 SQL Flow

SQL Flow позиционируется как «DuckDB для потоковых данных» — лёгковесный движок stream-обработки, позволяющий описывать весь pipeline единственным языком SQL и служащий компактной альтернативой Apache Flink.

🔍 Ключевые возможности:

- Источники (Sources): Kafka, WebSocket-стримы, HTTP-webhooks и др.
- Приёмники (Sinks): Kafka, PostgreSQL, локальные и S3-подобные хранилища, любые форматы, которые поддерживает DuckDB (JSON, Parquet, Iceberg и т.д.).
- SQL-обработчик (Handler): встраивает DuckDB + Apache Arrow; поддерживает агрегаты, оконные функции, UDF и динамический вывод схемы.
- Управление окнами: in-memory tumbling-windows, буферные таблицы.
- Наблюдаемость: встроенные Prometheus-метрики (с релиза v0.6.0).

🔗 Архитектура

Конвейер описывается YAML-файлом с блоками `source → handler → sink`.
Во время выполнения:

1. Source считывает поток (Kafka, WebSocket …).
2. Handler выполняет SQL-логику в DuckDB.
3. Sink сохраняет результаты в выбранное хранилище.

Быстрый старт (≈ 5 минут)


# получить образ
docker pull turbolytics/sql-flow:latest

# тестовая проверка конфигурации
docker run -v $(pwd)/dev:/tmp/conf \
-v /tmp/sqlflow:/tmp/sqlflow \
turbolytics/sql-flow:latest \
dev invoke /tmp/conf/config/examples/basic.agg.yml /tmp/conf/fixtures/simple.json

# запуск против Kafka
docker-compose -f dev/kafka-single.yml up -d # поднять Kafka

docker run -v $(pwd)/dev:/tmp/conf \
-e SQLFLOW_KAFKA_BROKERS=host.docker.internal:29092 \
turbolytics/sql-flow:latest \
run /tmp/conf/config/examples/basic.agg.mem.yml --max-msgs-to-process=10000


Github

@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍115🔥4
🛢️ SQL-задача с подвохом: GROUP BY и скрытая ловушка

Условие:

Есть таблица orders:

| id | customer_id | amount | status |
|-----|-------------|--------|-----------|
| 1 | 101 | 200 | completed |
| 2 | 102 | 150 | NULL |
| 3 | 101 | 300 | completed |
| 4 | 103 | NULL | completed |
| 5 | 102 | 100 | completed |
| 6 | 101 | 250 | NULL |

Задача: найти всех клиентов, у которых сумма заказов больше 500.

Ты пишешь запрос:


SELECT customer_id, SUM(amount) as total
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 500;


Вопрос:
Какие клиенты вернутся? Есть ли тут подвох? Что произойдёт с заказами, где amount или statusNULL?

🔍 Подвох:

На первый взгляд запрос правильный: мы группируем по клиентам и суммируем их заказы. Но вот критичные моменты:

1️⃣ Что происходит с NULL в `amount`?

В SQL агрегатные функции (например, SUM) игнорируют NULL значения. Это значит:

- Заказ id=4 (`amount = NULL`) не участвует в суммировании.
- Заказ id=6 (`amount = 250`) участвует, потому что amount не NULL.

2️⃣ Считаем по каждому клиенту:

- customer_id=101:
- id=1: 200
- id=3: 300
- id=6: 250
Итого: 200 + 300 + 250 = 750

- customer_id=102:
- id=2: 150
- id=5: 100
Итого: 150 + 100 = 250

- customer_id=103:
- id=4: NULL (игнорируется)
Итого: 0

3️⃣ Кто попадёт в результат:

Только customer_id=101 (с суммой 750 > 500).

---

Результат:

| customer_id | total |
|-------------|--------|
| 101 | 750 |

---

💥 Подвох #2:

Допустим ты случайно написал:


HAVING SUM(amount) IS NOT NULL AND SUM(amount) > 500;


Кажется логичным? А вот нет: SUM всегда возвращает 0 (не NULL), даже если у клиента нет заказов с ненулевой суммой.

➡️ Даже клиент с только NULL значениями (например, customer_id=103) получит SUM(amount) = 0, а не NULL.

Это частая ловушка:
COUNT, SUM, AVG игнорируют NULL внутри, но результат НЕ NULL (обычно 0 или NULL в зависимости от агрегата).

---

🛠 Что ещё важно:

• Если хочешь учитывать только выполненные заказы (status = 'completed'), нужно добавить:


WHERE status = 'completed'


⚠️ Не пиши условие в HAVING для фильтрации строк — лучше фильтровать через WHERE до группировки.

Вывод:

- Агрегатные функции типа SUM игнорируют NULL внутри группы.
- SUM возвращает 0, даже если все значения NULL (НЕ NULL, как думают многие).
- HAVING применяется ПОСЛЕ группировки, а WHERE — ДО.
- Ошибки часто возникают, если условие на фильтр пишут в HAVING вместо WHERE.

💡 Бонус-вопрос:
Что будет, если заменить SUM(amount) на COUNT(amount) в SELECT и HAVING? И как это повлияет на клиентов с NULL значениями?

@sqlhub
🔥16👍75
2025/07/09 02:39:35
Back to Top
HTML Embed Code: