В мире больших данных
245 subscribers
34 photos
5 files
54 links
Полезные заметки о системном анализе в мире больших данных. Если вам интересны Big Data, DWH, SQL и как навести порядок в данных — заглядывайте. Будет интересно и по делу.

Автор: @JuliaMur
加入频道
ANY_VALUE: функция для упрощения GROUP BY запросов

Привет! Сегодня расскажу про функцию ANY_VALUE в SQL. Она помогает упростить GROUP BY запросы, особенно когда вы работаете с большими наборами данных.

Если вы работали с агрегатными функциями и группировками GROUP BY, то, вероятно, сталкивались с ограничениями при выборе столбцов.

Представьте, у вас есть не очень нормализированная витрина с заказами (всё также рекомендую смотреть таблички в десктоп версии или развернуть телефон горизонтально🥲):
| ord_id | cust_id | cust_nm | product    | qty | price |
|--------|---------|---------|------------|-----|-------|
| 101 | 1 | Иван | Ноутбук | 2 | 1500 |
| 102 | 2 | Ольга | Смартфон | 1 | 800 |
| 103 | 1 | Иван | Планшет | 1 | 600 |
| 104 | 3 | Анна | Наушники | 3 | 150 |
| 105 | 2 | Ольга | Умные часы | 2 | 400 |


И перед вами стоит задача получить общую сумму заказов для каждого клиента:
SELECT cust_id, SUM(qty * price) as total_amount
FROM orders
GROUP BY cust_id


Но что если мы захотим добавить в результат customer name (cust_nm)? Получим ошибку, потому что cust_nm не входит в GROUP BY и не используется в агрегатной функции. Вот здесь и приходит на помощь ANY_VALUE:
SELECT 
cust_id,
ANY_VALUE(cust_nm) as customer_name,
SUM(qty * price) as total_amount
FROM orders
GROUP BY cust_id


Этот запрос выполнится без ошибок. ANY_VALUE говорит базе данных: "Возьми любое значение cust_nm для каждой группы cust_id".
Важно понимать, что ANY_VALUE не гарантирует, какое именно значение будет выбрано. Оно может меняться от запуска к запуску. Поэтому используйте эту функцию, только когда вам не важно, какое именно значение будет возвращено, или если вы уверены, что внутри группы значения одинаковы.

ANY_VALUE помогает оптимизировать запросы. В некоторых СУБД она дает понять оптимизатору, что порядок выбора значений не важен, что может привести к более эффективному плану выполнения, чем при использовании min-max на группе.

Однако, не все СУБД поддерживают ANY_VALUE. В PostgreSQL, например, как раз таки придётся использовать min или max:
SELECT 
cust_id,
MIN(cust_nm) AS customer_name,
SUM(qty * price) AS total_amount
FROM orders
GROUP BY cust_id;


ANY_VALUE — полезная функция для упрощения агрегатных запросов, когда точное значение не имеет значения. Главное — использовать его осознанно и понимать, когда его применение оправдано.

#sql
Please open Telegram to view this post
VIEW IN TELEGRAM
👍54
UNION и UNION ALL. Так ли всё просто?

Маленькая заметка-напоминалка.

Операторы UNION и UNION ALL в SQL отвечают за объединение результатов нескольких запросов. При этом просто UNION выводит только уникальные строки в запросах, то с ALL выведет абсолютно все строки, включая возможные дубли.

Как операторы объединения работают с NULL?
UNION — объединит похожие строки, содержащие NULL в 1 (считая, что это дубли), а UNION ALL оставит все строки.

Ещё несколько особенностей:
1. Набор полей у всех объединяемых запросов должен быть одинаков.
2. Важно! При использовании UNION снижается производительность, так как приходится сканировать результат на наличие дублей. В случае, если в результатах объединения предсказуемо нет дублирующихся полей, предпочтительнее использовать UNION ALL.

#sql #null
👍5
QUALIFY: фильтруем результаты оконных функций

QUALIFY — SQL-конструкция, которая позволяет отфильтровать результаты после применения оконных функций 😍

Она работает аналогично WHERE, но с той разницей, что QUALIFY применяется после оконных функций, а WHERE — до них.

Напомню порядок выполнения запроса:
1. From
2. Where
3. Group by
4. Having
5. Window
6. QUALIFY
7. Distinct
8. Order by
9. Limit

Представим, что у нас есть таблица продаж, и мы хотим выбрать топ-5 продаж по каждой категории товаров. С помощью QUALIFY это можно сделать просто и эффективно:

SELECT
category,
product,
sales_amount,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales_amount DESC) AS rn
FROM sales
QUALIFY rn <= 5;

В этом запросе сначала нумеруем продажи в каждой категории по убыванию суммы продаж с помощью ROW_NUMBER(). Затем с помощью QUALIFY оставляем только первые пять записей в каждой категории. Легко и понятно.

Почему нельзя использовать WHERE вместо QUALIFY? Потому что WHERE фильтрует данные до выполнения оконных функций, а нам нужно отфильтровать данные после. Если попробовать использовать WHERE rn <= 5, SQL не поймёт, что такое rn, потому что на этапе выполнения WHERE эта колонка ещё не создана.

Конечно, можно использовать QUALIFY и с другими оконными функциями, например, RANK():

SELECT
employee_id,
department,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC)
AS salary_rank
FROM employees
QUALIFY salary_rank = 1;

Этот запрос выберет сотрудников с наивысшей зарплатой в каждом отделе.

Стоит отметить, что QUALIFY поддерживается не во всех СУБД. Например, в Snowflake и Teradata эта функция есть, а в PostgreSQL или MySQL её нет. В таких случаях приходится использовать подзапросы или CTE (Common Table Expressions).

Например так:

WITH ranked_sales AS (
SELECT
category,
product,
sales_amount,
RANK() OVER (PARTITION BY category ORDER BY sales_amount DESC) AS rn
FROM sales
)
SELECT *
FROM ranked_sales
WHERE rn <= 5;

Согласитесь, что использование QUALIFY делает код более кратким и читаемым. Используйте его, когда это возможно 😎

#sql
Please open Telegram to view this post
VIEW IN TELEGRAM
5🔥4
Прокачиваем SQL-запросы с фишками оконных функций

Многие аналитики активно используют базовые оконные функции, но не всегда знают, как применять такие инструменты, как фрейм окна (window frame). А ведь это сделает запросы ещё более читабельными и эффективными. Давайте разбираться вместе.

Фрейм определяет набор строк для вычислений оконной функции относительно текущей строки.


В этой статье рассмотрим два способа определить, какие строки включать в окно для расчетов: ROWS и RANGE.

Начнем с ROWS BETWEEN. Эта конструкция работает с физическими строками и помогает анализировать конкретное количество записей до и после текущей строки. Чтобы стало понятнее, рассмотрим пример: мы хотим рассчитать скользящую сумму продаж за последние 3 дня, включая текущий день. Для этого подсчёта нам необходимо от каждой строки отсчитать две строки назад и суммировать значения продаж за эти дни.


| sales_date | sales_amount |
|------------|--------------|
| 2024-01-01 | 100 |
| 2024-01-02 | 150 |
| 2024-01-03 | 200 |
| 2024-01-04 | 250 |



SELECT
sales_date,
sales_amount,
SUM(sales_amount) OVER (
ORDER BY sales_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS rolling_sum
FROM sales_daily;


Результат:

| sales_date | sales_amount | rolling_sum |
|------------|--------------|-------------|
| 2024-01-01 | 100 | 100 |
| 2024-01-02 | 150 | 250 |
| 2024-01-03 | 200 | 450 |
| 2024-01-04 | 250 | 600 |


Выражением ROWS BETWEEN 2 PRECEDING AND CURRENT ROW мы буквально говорим, что нужно от каждой строки отсчитывать две строки назад и суммировать значения продаж за эти дни (включая значение в текущей). Таким образом, каждая строка будет содержать сумму продаж за текущий день и два предыдущих. Важно! В нашей исходной витрине данные уже сгруппированы по дням, поэтому в данном случае корректно говорить Х дней назад, но по сути мы говорим про строки.

RANGE BETWEEN работает иначе. Он группирует строки по значению, а не по их физическому положению. Это бывает полезно, когда нужно учитывать все строки с определённым диапазоном значений относительно текущей строки. Не понятно? Перейдём к примеру.

Нужно рассчитать среднюю цену за последние 3 дня от каждой даты:

| price_date | stock_price |
|-------------|-------------|
| 2024-01-01 | 100 |
| 2024-01-01 | 102 |
| 2024-01-02 | 105 |
| 2024-01-04 | 103 |
| 2024-01-04 | 106 |
| 2024-01-06 | 110 |



SELECT
price_date,
stock_price,
AVG(stock_price) OVER (
ORDER BY price_date
RANGE BETWEEN INTERVAL '3' DAY PRECEDING AND CURRENT ROW
) AS avg_price_3days
FROM stock_prices;


Результат:

| price_date | stock_price | avg_price_3days |
|------------|-------------|-----------------|
| 2024-01-01 | 100 | 101.00 |
| 2024-01-01 | 102 | 101.00 |
| 2024-01-02 | 105 | 102.33 |
| 2024-01-04 | 103 | 103.20 |
| 2024-01-04 | 106 | 103.20 |
| 2024-01-06 | 110 | 106.33 |


При использовании RANGE все строки с одинаковым значением столбца, указанного в ORDER BY, обрабатываются вместе. А RANGE BETWEEN INTERVAL '3' DAY PRECEDING AND CURRENT ROW говорит о том, что нужно посчитать значения в фрейме с интервалом 3 дня до текущей строки.

Обратите внимание, что даты 2024-01-03 и 2024-01-05 отсутствуют в таблице. Когда мы используем указанный RANGE BETWEEN, SQL ищет все строки, где price_date находится в диапазоне от текущей даты минус календарных 3 дня, то есть учитывает только имеющиеся даты в этом диапазоне.

Поняли разницу? RANGE учитывает все записи в указанном временном интервале, даже если их много. А ROWS всегда отсчитывает фиксированное количество строк.

Естественно обе функции можно использовать не только при расчётах связанными с датами. Давайте в одной из следующих статей рассмотрим другие примеры. Фреймы кажутся той темой, которой стоит уделить особое внимание.

#sql
👍4
Обожаю Snowflake за множество классных функций, делающих sql-код более читабельным. При этом специально "переучиваться" для работы в снежке не нужно, так как он поддерживает всем знакомый (ну я надеюсь) стандарт ANSI SQL. Просто каждый раз в работе (или листая документацию) ты находишь прикольные фишки, которые хочется использовать.

Например, как бы вы посчитали, сколько задач разработчиков завершено до дедлайна, а сколько — нет? Обычно пришлось бы писать что-то вроде:

SELECT
developer,
SUM(CASE WHEN completion_date <= deadline THEN 1 ELSE 0 END) AS on_time_tasks,
SUM(CASE WHEN completion_date > deadline THEN 1 ELSE 0 END) AS late_tasks
FROM tasks
GROUP BY developer;


В Snowflake этот подсчёт выглядит куда лаконичнее:

SELECT
developer,
COUNT_IF(completion_date <= deadline) AS on_time_tasks,
COUNT_IF(completion_date > deadline) AS late_tasks
FROM tasks
GROUP BY developer;


Кажется, что это незначительная мелочь? Но насколько приятнее и понятнее становится разбирать тысячи строк какого-нибудь сложного аналитического запроса. Краткость — сила!

#snowflake #sql
👍2🔥1
GROUPING SETS для упрощения агрегации

Мы группируем данные десятки раз в день: по датам, категориям, клиентам или нескольким полям сразу. Но что, если нужно получить несколько уровней агрегации в одном результате? Объединять три разных запроса через UNION ALL? Писать вложенные подзапросы? Такой сценарий превращает простую задачу в головоломку с кучей повторяющегося кода 🔄

Теперь представьте: один запрос возвращает и детализацию, и промежуточные итоги, и общую сумму. И всё это без дублирования логики и потери производительности. Это не магия — это GROUP BY GROUPING SETS. Спойлер: после него вы вряд ли захотите возвращаться к старому подходу.

Синтаксис:

SELECT column1, column2, AGG_FUNC(column3) AS aggregate_result
FROM table_name
GROUP BY GROUPING SETS
(
(column1),
(column2),
(column1, column2),
() -- итоговая строка для всех данных
);


Итак, у нас есть таблица с заказами, и нужно вывести витрину продаж: по дням, категориям, дням и категориям, а также общие продажи.


| order_id | order_dt | category | price |
|----------|------------|-------------|-------|
| 1 | 2025-02-01 | Книги | 100 |
| 2 | 2025-02-01 | Книги | 200 |
| 3 | 2025-02-01 | Электроника | 700 |
| 4 | 2025-02-02 | Книги | 150 |
| 5 | 2025-02-02 | Электроника | 250 |
| 6 | 2025-02-02 | Электроника | 550 |


Запрос:

SELECT
order_dt,
category,
SUM(price) AS total_sum
FROM orders
GROUP BY GROUPING SETS
(
(order_dt, category), -- Группировка по дням и категориям
(order_dt), -- по дням
(category), -- по категориям
() -- Итоговая строка
);


Результат:

| order_dt | category | total_sum |
|------------|-------------|-----------|
| 2024-01-01 | Книги | 300 |
| 2024-01-01 | Электроника | 700 |
| 2024-01-02 | Книги | 150 |
| 2024-01-02 | Электроника | 800 |
| 2024-01-01 | NULL | 1000 |
| 2024-01-02 | NULL | 950 |
| NULL | NULL | 1950 |
| NULL | Книги | 450 |
| NULL | Электроника | 1500 |


🔵Полные строки (order_dt и category заполнены) — детализированные данные.
🔵Строки с order_dt и NULL показывают суммы по каждому дню.
🔵Строки с category и NULL показывают суммы по каждой категории.
🔵Строка с двумя NULL — общая сумма.

Если нужно определить, какие строки являются результатом группировки, используйте функцию GROUPING(). Она возвращает 1 там, где значение агрегировано.

Пример:

SELECT
order_dt,
category,
SUM(price) AS total_sales,
GROUPING(order_dt) AS is_dt_agg,
GROUPING(category) AS is_cat_agg
FROM orders
GROUP BY GROUPING SETS
(
(order_dt), -- Группировка по дням
(category), -- Группировка по категориям
() -- Итоговая строка
);

| order_dt | category | total_sales | is_dt_agg | is_cat_agg |
|------------|------------|-------------|-----------|------------|
| 2024-01-01 | NULL | 1000 | 0 | 1 |
| 2024-01-02 | NULL | 950 | 0 | 1 |
| NULL | NULL | 1950 | 1 | 1 |
| NULL | Книги | 450 | 1 | 0 |
| NULL | Электроника| 1500 | 1 | 0 |


Почему GROUPING SETS лучше UNION ALL?
один запрос вместо нескольких
оптимизация выполнения — СУБД сканирует таблицу один раз и для каждой строки вычисляет все группировки параллельно
читабельность кода

поддерживаются не все диалекты SQL (но основные — PostgreSQL, Oracle, SQL Server, Snowflake, BigQuery — да)

GROUP BY GROUPING SETS полезен для отчетности и аналитических анализов, где нужны сводные данные разной детализации. Это инструмент работает:
🟢 удобно: меньше кода, меньше ошибок
🟢 быстро: один проход по данным
🟢 гибко: возможны любые комбинации группировок

#sql
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍3
SQL под капотом: как выполняются запросы

Знаете ли вы, что происходит под капотом СУБД, когда вы выполняете SQL-запрос? База данных запускает целый процесс, шаг за шагом превращая код в набор данных. Каждая команда проходит проверку, оптимизацию, выполнение, обработку и вывод результата. Давайте посмотрим на каждый из этапов:

1. Query Parsing (разбор запроса)
Сначала сервер проверяет, правильно ли написан запрос. Проводит так называемый синтаксический анализ. Ошиблись в запятой или перепутали порядок ключевых слов? Получите ошибку.

После синтаксического анализа начинается семантический разбор: существуют ли таблицы и колонки, есть ли у вас права на запрос? Если все ок, база строит parse tree.

Parse Tree — это иерархическое представление запроса, где каждый узел — отдельная операция (например, фильтр, join, сортировка). Это облегчает работу оптимизатора и позволяет строить разные планы выполнения.

2. Query Optimization (оптимизация запроса)
На этом этапе в работу вступает умный планировщик. Он оценивает различные стратегии выполнения запроса, чтобы определить наиболее эффективную и менее ресурсоёмкую. Оптимизаторы сильно отличаются от СУБД к СУБД, но, к примеру, в Snowflake он, действительно, умный и даже плохо написанный запрос в большинстве случаев "переписывает" оптимально самостоятельно (это, конечно, не значит что стоит писать запросы как попало 👿).

Оптимизатор, в зависимости от СУБД может проверять:
Как соединять таблицы — Nested Loop, Hash Join, Merge Join?
Как фильтровать и сортировать данные?
Использовать индексы или нет?

Оптимизатор анализирует статистику таблиц: сколько строк, какие значения чаще встречаются, какие индексы есть. Он перебирает варианты и выбирает наилучший.

3. Query Execution (выполнение запроса)
После этого база данных начинает пошагово выполнять запрос, согласно выбранному плану.

Запросы могут выполняться через:
🔵 Table Scan — полный перебор строк в таблице (долго).
🔵 Index Seek — точечный поиск через индекс (быстро, но требует индекса).

4. Извлечение или изменение данных
Если наш запрос извлекает данные (SELECT - Data Query Language), база выбирает нужные строки из таблиц и формирует результат. Если же запрос изменяет данные (INSERT, MERGE, UPDATE или DELETE - Data Manipulation Language), информация в таблице обновляется, удаляется или дополняется.

5. Формирование результата
Когда SQL-движок собрал нужные строки, он финально формирует итоговый результат: сортирует, группирует, выполняет агрегатные вычисления. Однако часть агрегаций, особенно в запросах с GROUP BY, может выполняться еще на этапе извлечения данных, если движок решит, что это эффективнее. То есть это зависит от плана выполнения запроса и используемого метода обработки.

6. Результат
Когда всё готово, результат возвращается в клиентское приложение, которое уже отображает его пользователю.

Для SELECT-запросов, если данных много, они передаются частями, чтобы не перегружать память.
Некоторые базы поддерживают Lazy Execution — строки выгружаются только при необходимости.


Как видите, написанный запрос запускает целые механизмы внутри СУБД. Каждый этап играет свою роль: разбор проверяет синтаксис на ошибки, оптимизатор выбирает самый быстрый путь, выполнение шаг за шагом приводит к нужному результату, а передача данных гарантирует, что вы получите ответ в удобной форме, ничего не потеряв.

Не всегда имеет смысл знать, что происходит под капотом, но хотя бы верхнеуровневое понимание помогает нам самим работать эффективнее. Если что-то идет не так, вы будете знать, где искать проблему и как ее решить. Понимание происходящего — ключ к написанию быстрых и оптимизированных запросов.

#sql
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6👎21🤡1
Разбираемся с дублями

Если вашу выборку нужно почистить от дублей, вы можете сделать это очень просто:


SELECT *
FROM your_table
QUALIFY ROW_NUMBER() OVER(PARTITION BY column_id ORDER BY column_dt DESC) = 1;


В результате получим в выводе только уникальные строки (вместо *, конечно же, указываем корректный список полей).

QUALIFY + ROW_NUMBER() = никаких лишних подзапросов 🙃

Недостаток: пока что работает не во всех СУБД 🥲

Если СУБД не поддерживает оператор QUALIFY, можем чистить так:

WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY column_id ORDER BY column_dt DESC) AS rn
FROM your_table
)
SELECT *
FROM cte
WHERE rn = 1;


P.S. Про сам QUALIFY я уже писала здесь.

#sql
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43🔥2
Иногда приходится разбирать чужие sql-запросы и периодически сталкиваюсь с различными ошибками. Сегодня хочу рассказать о трёх наиболее распространённых.

Некорректная работа с NULL
Я уже много раз писала, NULL — не просто пустота, это неизвестность. Поэтому нельзя сравнивать с NULL в лоб. Запрос вам ошибку не выдаст, но отработает некорректно.


-- неправильно:
SELECT * FROM users WHERE age = NULL;
SELECT * FROM users WHERE age != NULL;

-- правильно:
SELECT * FROM users WHERE age IS NULL;
SELECT * FROM users WHERE age IS NOT NULL;


Также при подсчёте количества строк COUNT(column_name) пропустит все NULL-значения. Поэтому если нужно посчитать прям вообще всё используйте COUNT(*).


-- считает количество заполненных номеров:
SELECT COUNT(phone) FROM users;

-- считает все строки, в том числе с NULL:
SELECT COUNT(*) FROM users;


Больше про #null я писала в постах с соответствующим тегом) на собесах часто про это спрашивают, но уделить внимание теме, конечно же, стоит не только поэтому.

Неправильное использование оператора BETWEEN
Ещё часто вижу, как забывают об особеннстях BETWEEN, забывая, что он включает и верхнюю, и нижнюю границы диапазона. Это может привести к дублированию данных или их пропуску при последовательной выборке.


-- пример кода с ошибкой:
-- выборка за 1 марта о полю типа дата-время
SELECT * FROM orders WHERE order_dttm BETWEEN '2024-03-01' AND '2024-03-02';
-- Выборка за 2 марта
SELECT * FROM orders WHERE order_dttm BETWEEN '2024-03-02' AND '2024-03-03';


В этом примере заказы, созданные ровно в полночь 2 марта (2024-03-02 00:00:00), будут включены в обе выборки! Лучше использовать явные полуинтервалы:

-- правильно:
-- выборка за 1 марта
SELECT * FROM orders WHERE order_dttm >= '2024-03-01' AND order_dttm < '2024-03-02';
-- выборка за 2 марта
SELECT * FROM orders WHERE order_dttm >= '2024-03-02' AND order_dttm < '2024-03-03';


Но если сильно хочется BETWEEN, то:

-- выборка за 1 марта
SELECT * FROM orders WHERE order_dttm BETWEEN '2024-03-01 00:00:00' AND '2024-03-01 23:59:59';
-- выборка за 2 марта
SELECT * FROM orders WHERE order_dttm BETWEEN '2024-03-01 00:00:00.000' AND '2024-03-01 23:59:59.999';


Да, про миллисекунды забывать не нужно, а то можно что-то потерять. И всё-таки проще использовать полуинтервалы)

Ошибки в логических операторах
Ещё часто забывают про приоритеты при использовании AND и OR в одном условии. В SQL сначала выполняются все AND, а затем уже OR.
Например, нужно найти все транзакции на сумму больше 100.000, которые имеют статус "completed" и при этом либо от премиум-пользователя, либо оплачены кредитной картой.

-- неправильно:
SELECT * FROM transactions
WHERE amount > 100000
AND status = 'completed'
AND user_type = 'premium' OR payment_method = 'credit_card'


По правилам SQL операторы AND приоритетнее. Поэтому запрос интерпретируется так:

SELECT * FROM transactions
WHERE (status = 'completed' AND amount > 100000 AND user_type = 'premium')
OR (payment_method = 'credit_card')


То есть мы получим все завершённые транзакции премиум-пользователей с суммой больше 100000, плюс абсолютно все транзакции с кредитных карт (даже незавершённые и с маленькими суммами).

Так мы получим именно то, что хотели:

-- правильно:
SELECT * FROM transactions
WHERE status = 'completed'
AND amount > 100000
AND (user_type = 'premium' OR payment_method = 'credit_card')


В целом, проще лишний раз указать скобки, чем запутаться и получить ошибочный результат.

Кому-то кажется очевидным, но такие вещи, действительно, встречаются. А с какими ошибками в sql вы часто сталкиваетесь?

#sql
👍6🔥5
Занималась тут оптимизацией чужого запроса. И вот вроде бы знаешь базу и хочешь её применить, но оптимизатор всегда оказывается хитрее 🙂

Среди прочего, пыталась применить одно из главных правил оптимизации — predicate pushdown. Это когда мы поднимаем условия фильтрации как можно выше, чтобы заранее уменьшить объем данных. Так вот, вынесла в cte фильтрацию одной таблички (~2GB), а в другом cte уже шла работа с отфильтрованными данными — джойны и тп. Смотрю в план запроса и вижу фигу, что снежок (snowflake) всё равно сначала сканирует таблицу целиком, затем джойнит, и только после этого фильтрует 😵 причём аналогичный сценарий на другой, но бОльшей таблице (~в 8GB) отрабатывает как надо 🥲 Видимо, размер данных или внутренняя статистика влияют на решения cost-based оптимизатора.

Никаких инсайтов в этой заметке вам не дам, но в очередной раз убеждаюсь: важно уметь читать (и понимать) планы запросов и анализировать query profile. Не всегда логичные на первый взгляд шаги оптимизации работают как ожидается. И не только от СУБД к СУБД поведение может разительно отличаться, но и даже в рамках таблиц в одном хранилище. Экспериментируйте и тестируйте на реальных данных 🤖

P.S. Тем, кто хочет использовать для анализа планов гпт, всё же советую сначала самостоятельно научиться их читать, т.к. LLM всё ещё склонны к галлюцинациям. Как говорится: "на ИИ надейся, да сам не плошай".

#sql #snowflake
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12