Избавиться от подзапросов в SQL

269
17 декабря 2017, 08:24

Есть запрос SQL, проблемная часть которого выглядит так:

SELECT DISTINCT chb.ck_filename, 
    ...
    (select gvpl.gvpl_descr
     from gviya gv1, gviya_place gvpl 
     where gv1.gv_gvpl_id = gvpl.gvpl_id
        and gv1.gv_id = (select max(gviya.gv_id)
                         from gviya 
                         where gv_trid = chb.ck_trid
                        )
    ) as gv_place,
    ...

Дальше идет выборка остальных полей. Так как в таблице очень много данных, то вложенный запрос выполняется очень долго. Понятно, что он должен выполниться для каждой строчки, то есть, допустим, если основной запрос вернет 1000 строчек, то и вложенный выполнится 1000 раз, увеличивая общее время выполнения в 1000 раз. Надо как-то избавиться от вложенного запроса, при этом все равно получая gvpl_descr для max(gviya.gv_id) в каждой строчке. Спасибо за ответы.

Answer 1

Ну, можно начать с самого "нижнего" подзапроса, группировать его

select max(gviya.gv_id)
from gviya 
group by gv_trid

Затем его присоединить к вышестоящему запросу

select gvpl.gvpl_descr
from gviya gv1, gviya_place gvpl,
  (select gv_trid, max(gviya.gv_id) max_gv_id
   from gviya 
   group by gv_trid) group_gviya  
where gv1.gv_gvpl_id = gvpl.gvpl_id
  and group_gviya.gv_trid = chb.ck_trid
  and gv1.gv_id = group_gviya.max_gv_id

Либо можно, наверное, попробовать как-то воспользоваться оконными функциями

Answer 2

Можно переписать подзапрос в виде:

select gvpl.gvpl_descr
  from gviya gv1, gviya_place gvpl 
 where gv1.gv_gvpl_id = gvpl.gvpl_id
   and gv_trid = chb.ck_trid
 order by gv1.gv_id desc
 limit 1

Таким образом мы избавимся от подзапроса второго уровня. Но его выполнение все равно будет достаточно медленным. В идеале надо избавится вообще от подзапросов в списке выборки, например сгруппировав и заранее получив все максимальные gv_id, как предложено в ответе @Vasil Baymurzin. Но такой подход даст существенный выигрыш только если весь целый запрос выбирает бОльшую часть всех существующих gv_trid. Если это не так, надо смотреть в сторону обеспечения получения уникальных строк всего запроса, а потом подклейки к ним (join) всех подходящих строк gviya с последующей фильтрацией на максимальные с помощью использования переменных или трюков вроде substr( max( concat() ) ), как предложено например тут.

В общем для правильной оптимизации, информации, приведенной в вопросе, не достаточно. Необходимо видеть в комплексе весь запрос, всю структуру БД, смотреть планы выполнения и экспериментировать.

READ ALSO
Получение скриншота структуры внутри TreeView

Получение скриншота структуры внутри TreeView

Как получить скриншот всей структуры дерева TreeView? Делал это следующим кодом, но он получает изображение только видимой области дерева:

227
Как преобразовать json в объект?

Как преобразовать json в объект?

Есть json, для которого Visual Studio сгенерировал вот такой класс

327
Метод расширения для UIElementCollection

Метод расширения для UIElementCollection

Есть метод расширения:

267