Понять, как СУБД выполняет запрос IN - PullRequest
1 голос
/ 08 марта 2020

У меня была проблема с производительностью в моем запросе, я нашел решение, но я не очень понимаю, почему у меня возникла проблема, поэтому кто-то может мне это объяснить ... У меня есть 2 таблицы: Поток (несколько строк по миллионам) и Устройства (<10 строк). Ожидаемый результат запроса: последняя строка из таблицы потоков для каждого устройства, а также данные из таблицы устройств. </p>

Мой первый запрос:

SELECT * FROM Flow WHERE FlowId IN (SELECT MAX(FlowId) FROM Flow GROUP BY DeviceId)

Время выполнения> 200 секунд.

Если я сначала выполню подзапрос (получаю результат за 0,01 секунды), а затем использую результат в запросе:

SELECT * FROM Flow WHERE FlowId IN (93143922, 93143921)

У меня результат меньше, чем за секунду ... Как СУБД выполняет первый запрос ? Я думал, что поток: сначала выполнить подзапрос, а затем использовать результат в «основном» запросе? Если я делаю эти шаги вручную, запрос выполняется быстро, так что это абсолютно не так: (

Мое решение таково:

SELECT * FROM Flow f
INNER JOIN Devices d on d.DevicesId = f.DeviceId
INNER JOIN (SELECT MAX(FlowId) as FlowId FROM FLOW GROUP BY DeviceId) maxFlow
 ON maxFlow.FlowId = f.FlowId

Но мне нужно знать, как он работает с IN:)

Ответы [ 2 ]

1 голос
/ 08 марта 2020

Ваш случай будет иметь место только для действительно плохого оптимизатора или если синтаксис БД над произвольным полем не является стандартным. Обратите внимание, что внутри предложения IN вы помещаете поля FlowId и DeviceId. По умолчанию большинство оптимизаторов могут понимать, что это относится к значению таблицы констант внутри предложения IN, но некоторые могут подумать, что на это влияет область действия снаружи, поэтому детали внутри IN необходимо пересматривать для каждой строки внешнее сканирование.

Хороший оптимизатор: 1. вычислить результат IN 2. l oop через каждую строку для внешнего сканирования с использованием результата 2 / или поиск результата по индексу, если FlowId является первым ключом любого индекса

Неверный оптимизатор (или ограничение синтаксиса): 1. l oop через каждый ряд для внешнего сканирования 2. для каждой строки вычислите результат IN 3. вычислите результат WHERE, используя результат 2

Плохой случай действительно плох, поскольку он полностью сканирует вашу таблицу столько раз, сколько строк в ней.

1 голос
/ 08 марта 2020

IN иногда плохо оптимизируется. Для этого есть несколько причин:

  • Результаты из подзапроса должны быть "дедуплицированы". Возможно, вы знаете, что дубликатов нет, но это сложно понять движку.
  • Двигателю сложно угадать, сколько строк будет возвращено из IN

Я считаю, что коррелированный подзапрос часто имеет наилучшую производительность:

SELECT f.*
FROM Flow f
WHERE f.FlowId = (SELECT MAX(f2.FlowId)
                  FROM Flow f2
                  WHERE f2.DeviceId = f.DeviceId
                 );

В частности, это может использовать индекс на Flow(DeviceId, FlowId).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...