Как понять, что SQL-запрос не оптимален?

Ответ

Неоптимальный запрос можно выявить по нескольким ключевым метрикам и признакам в плане выполнения.

Основные индикаторы проблем:

  1. Высокая нагрузка на ресурсы:

    • Длительное время выполнения (CPU TIME, ELAPSED TIME).
    • Большое количество логических и физических операций чтения (I/O).
  2. Неэффективные операции в плане запроса (Execution Plan):

    • Table/Index Scan вместо Index Seek. Это указывает на отсутствие подходящего индекса или его неиспользование.
    • Key Lookup (RID Lookup) — часто возникает при неполном покрывающем индексе, что приводит к дополнительным операциям поиска.
    • Сostly operators: Сортировка (Sort), хэширование (Hash Match) для больших наборов данных, параллелизм (Parallelism) для простых запросов.
    • Завышенные оценки числа строк (Estimated vs Actual Rows) — признак устаревшей статистики.

Практический анализ в SQL Server:

-- Включим сбор статистики по времени и I/O
SET STATISTICS TIME ON;
SET STATISTICS IO ON;

-- Выполним проблемный запрос
SELECT o.*, c.Name
FROM dbo.Orders o
JOIN dbo.Customers c ON o.CustomerId = c.Id
WHERE o.Status = 'Pending' AND o.CreatedDate > '2023-01-01';

-- После выполнения смотрим вкладку "Messages" для статистики.
-- Анализируем графический план выполнения (включается Ctrl+M или SET SHOWPLAN_XML ON).

Направления для оптимизации:

  • Индексы: Создать недостающие, проверить использование существующих, рассмотреть покрывающие (covering) или columnstore индексы для аналитики.
  • Рефакторинг запроса:
    • Избегать SELECT *, явно перечислять нужные столбцы.
    • Заменять коррелированные подзапросы на JOIN или APPLY.
    • Упрощать условия WHERE и JOIN.
  • Обновление статистики: UPDATE STATISTICS TableName;
  • Параметризация: Убедиться, что запросы параметризованы для эффективного повторного использования кэшированного плана.

Ответ 18+ 🔞

Ну, смотри, как определить, что твой запрос — это полный пиздец, который сервер еле тащит. Есть несколько явных признаков, по которым сразу видно, что пора оптимизировать.

На что смотреть, чтобы понять, что всё хуёво:

  1. Ресурсы жрёт как не в себя:

    • Время выполнения — если запрос выполняется не секунды, а минуты, это уже пиздец.
    • Операции ввода-вывода (I/O) — если он читает с диска гигабайты данных, это овердохуища нагрузки.
  2. План выполнения (Execution Plan) — тут всё как на ладони:

    • Table Scan / Index Scan вместо Index Seek. Это значит, что база, вместо того чтобы взять по индексу нужные строчки, тупо читает всю таблицу целиком, как дура. Пиздец, а не операция.
    • Key Lookup (RID Lookup) — это когда индекс есть, но он не покрывающий, и после него база лезет ещё и в основную таблицу за остальными данными. Двойная работа, ебать.
    • Дорогие операции: Сортировка (Sort) или хэширование (Hash Match) на огромных данных, или параллельное выполнение (Parallelism) для простого запроса — всё это тревожные звоночки.
    • Статистика ебнулась: Если в плане "Estimated Rows" (ожидалось) сильно отличается от "Actual Rows" (получилось на самом деле) — статистика по таблицам устарела, и оптимизатор строит план, как слепой котёнок.

Как это посмотреть в SQL Server на практике:

-- Включаем счётчики, чтобы посмотреть, сколько ресурсов сожрал запрос
SET STATISTICS TIME ON;
SET STATISTICS IO ON;

-- Выполняем наш подозрительный, возможно, ебанутый запрос
SELECT o.*, c.Name
FROM dbo.Orders o
JOIN dbo.Customers c ON o.CustomerId = c.Id
WHERE o.Status = 'Pending' AND o.CreatedDate > '2023-01-01';

-- После выполнения лезем на вкладку "Messages" — там будет вся статистика по времени и чтениям.
-- А главное — смотрим графический план выполнения (включается через Ctrl+M). Там все эти сканы и лукапы будут подсвечены, как ёлка.

И что с этим безобразием делать?

  • По индексам пройтись: Создать те, которых не хватает. Проверить, используются ли существующие. Для аналитических запросов посмотреть в сторону columnstore индексов — они могут творить чудеса.
  • Переписать сам запрос:
    • *Хули тебе `SELECT `?** Выписывай только нужные колонки, жадная жопа.
    • Избавляйся от кривых подзапросов, которые выполняются для каждой строчки. Часто их можно заменить на нормальный JOIN.
    • Упрощай условия в WHERE. Иногда простая перестановка условий может решить всё.
  • Обновить статистику: UPDATE STATISTICS TableName; — иногда после этого запрос начинает летать, потому что оптимизатор прозревает.
  • Проверить параметризацию: Убедись, что запросы не в виде "сырого" текста с константами, а параметризованные. Иначе план будет компилироваться каждый раз, и кэш планов будет засран.