SQL оптимизация
Нюансы по оптимизации SQL скриптов
Наткнулся на небольшую методичку по оптимизации SQL скриптов и решил выложить перевод, чтобы всегда был под рукой. Может быть, кому-то ещё пригодится.
Возможно, можно поспорить относительно некоторых утверждений автора, так что всё обсуждаемо.
Сперва привожу пример плохих практик с обозначением "Bad Practice", а затем - хороших "Good Practice".
# 1
Используйте 'REGEXP_LIKE' вместо оператора 'LIKE'
SELECT *
FROM
table1
WHERE
LOWER(item_name) LIKE '%samsung%'
OR
LOWER(item_name) LIKE '%xiaomi%'
OR
LOWER(item_name) LIKE '%iphone%'
OR
LOWER(item_name) LIKE '%huawei%'SELECT *
FROM
table1
WHERE
REGEXP_LIKE(LOWER(item_name), 'samsung|xiaomi|iphone|huawei')# 2
Используйте 'REGEXP_EXTRACT' вместо 'CASE-WHEN LIKE'
SELECT
CASE
WHEN CONCAT(' ', item_name, ' ') LIKE '%acer%' THEN 'Acer'
WHEN CONCAT(' ', item_name, ' ') LIKE '%advance%' THEN 'Advance'
WHEN CONCAT(' ', item_name, ' ') LIKE '%alfalink%' THEN 'Alfalink'
...
END AS brand
FROM item_list SELECT
REGEXP_EXTRACT(item_name, '(asus|lenovo|hp|acer|dell|zyrex|...)')
AS brand
FROM item_list# 3
Преобразуйте длинный список условий в операторе IN во временную таблицу
SELECT *
FROM table1 AS t1
WHERE
item_id IN (3363134, 5189076, ..., 4062349)SELECT *
FROM table1 AS t1
JOIN (
SELECT
item_id
FROM (
SELECT
SPLIT('3363134, 5189076, ...,' ,',') AS bar
)
CROSS JOIN
UNNEST(bar) AS t(item_id)
) AS table2 as t2
ON
t1.item_id = t2.item_idЭтот совет не выглядит 100% очевидным для меня. Возможно, запрос более оптимальный, но явно более сложный для восприятия. На мой взгляд, хорошую читаемость и простоту редактирования тоже нельзя сбрасывать со счетов при написании SQL скриптов.
# 4
Всегда сортируйте ваши JOIN от больших таблиц к меньшим таблицам
SELECT *
FROM
small_table
JOIN
large_table
ON small_table.id = large_table.idSELECT *
FROM
large_table
JOIN
small_table
ON large_table.id = small_table.id# 5
Используйте простые соединения в JOIN
Две таблицы со строкой даты, например, ‘2020-09-01’, но в одной из таблиц есть столбцы только для отдельных значений года, месяца, дня.
SELECT *
FROM
table1 t1
JOIN
table2 t2
ON t1.date = CONCAT(t2.year, '-', t2.month, '-', t2.day) SELECT *
FROM
table1 t1
JOIN (
SELECT
name,
CONCAT(t2.year, '-', t2.month, '-', t2.day) as date
FROM
table2 t2
) new
ON t1.date = new.date# 6
Всегда сортируйте "GROUP BY" по убыванию от атрибута/столбца с наибольшим количеством уникальных сущностей/значений к атрибуту/столбцу с наименьшим количеством уникальных сущностей/значений
SELECT
main_category,
sub_category,
item_id,
SUM(price)
FROM
table1
GROUP BY
main_category, sub_category, item_idSELECT
main_category,
sub_category,
item_id,
SUM(price)
FROM
table1
GROUP BY
item_id, sub_category, main_category# 7
Избегайте подзапросов в операторе WHERE
SELECT
SUM(price)
FROM
table1
WHERE
item_id IN (
SELECT
item_id
FROM
table2
)WITH t2 AS
(
SELECT
item_id
FROM
table2
)
SELECT
SUM(price)
FROM
table1 AS t1
JOIN
t2
ON t1.item_id = t2.item_id# 8
Используйте MAX вместо RANK
SELECT *
FROM (
SELECT
user_id,
RANK() OVER(ORDER BY prdate DESC) AS ranking
FROM
table1
) new
WHERE ranking = 1SELECT
user_id,
MAX(prdate)
FROM table1
GROUP BY 1# 9
Другие советы
- Используйте approx_distinct() вместо count(distinct) для очень больших
наборов данных - Используйте approx_percentile(метрика, 0,5) для медианы
- Избегайте UNION, где это возможно
- Используйте оператор WITH вместо вложенных подзапросов
© Kadek Dwi Pradnyana
April 2022
Перевод и адаптация:
@sinister_stark
December 2022