Декомпозиция
Декомпозиция — это разделение большого запроса на несколько запросов поменьше.
Возьмем для примера такой запрос:
SELECT
*
FROM (
SELECT *
FROM dm_ap2.dct_available_resources
WHERE cur = True
AND is_appointment_to_self = True
) ar
JOIN (
SELECT
toString(procedure_id) as procedure_id,
available_resource_id,
cur
FROM
dm_ap2.crs_available_resource_to_procedure
LIMIT 5000
) arp
ON arp.available_resource_id = ar.available_resource_id
Левая и правая части джоина выполняются сервером параллельно, и если таблицы достаточно большие, это может привести к высокой пиковой нагрузке.
Но нам не нужно чтение всей левой таблицы, нам достаточно прочитать только те available_resource_id, которые есть в правой таблице. Какими способами можно достичь этого?
Временные таблицы
Можно использовать временные таблицы. Тогда наш запрос будет выглядить так:
-- временная таблица 1, содержащая правую часть запроса
CREATE temporary table t1 as
SELECT
toString(procedure_id) as procedure_id,
available_resource_id,
cur
FROM
dm_ap2.crs_available_resource_to_procedure
LIMIT 5000;
-- временная таблица 2, содержащая левую часть запроса
CREATE temporary table t2 as
SELECT *
FROM
dm_ap2.dct_available_resources
WHERE available_resource_id in (
SELECT available_resource_id from t1 -- фильтруем id
)
AND cur = True
AND is_appointment_to_self = True;
SELECT *
FROM t1
JOIN t2
ON t1.available_resource_id = t2.available_resource_id
Мы создаем временные таблицы для обоих частей, а потом соединяем уже отфильтрованные таблицы. Таким образом мы уменьшили левую таблицу перед соединением, что сэкономит ресурсы.
Временные таблицы — фича ClickHouse, позволяющая создавать таблицы, которые живут в оперативной памяти компьютера. Они существуют только в контексте и перестают существовать после завершения запроса.
CTE
Второй способ — использование CTE (Common Table Expressions) для фильтрации левой таблицы перед джойном:
WITH arp AS (
SELECT
toString(procedure_id) AS procedure_id,
available_resource_id,
cur
FROM dm_ap2.crs_available_resource_to_procedure
LIMIT 5000
),
ar AS (
SELECT *
FROM dm_ap2.dct_available_resources
WHERE available_resource_id IN (
SELECT available_resource_id FROM arp # фильтруем здесь
)
AND cur = True
AND is_appointment_to_self = True
)
SELECT *
FROM ar
JOIN arp
ON arp.available_resource_id = ar.available_resource_id;
Такой подход имеет смысл, если таблица dct_available_resources очень большая, и фильтрация по available_resource_id значительно уменьшает объём данных. Если таблица не очень большая, лучше положиться на внутреннюю оптимизацию JOIN в ClickHouse.
Выводы
Если в запросе несколько подзапросов задумайтесь о декомпозиции. Особенно если в запросе несколько операторов FROM.
Если вы понимаете, что можно сократить количество строк, читаемых с диска — сделайте это.
Применяйте декомпозицию осторожно — не забывайте о читабельности кода и о логике запроса — она не должна нарушаться.
Last updated