Интерпретатор ХП

Модераторы: kdv, CyberMax

Ответить
dsd_corp
Сообщения: 8
Зарегистрирован: 12 ноя 2006, 15:32

Интерпретатор ХП

Сообщение dsd_corp » 12 ноя 2006, 16:00

Всем привет.

ХП в IB7.5, вот ее кусок:

Код: Выделить всё

  for select 
            ...
      from staff_units su
      where
           (
            (:int_id1=-1) or
            ( (:int_flt_type=1) and (su.id=:int_id1) ) or
            ( (:int_flt_type=2) and (su.pers_id=:int_id1) ) or
            ( (:int_flt_type=3) and (su.club_id=:int_id1) ) or
            ( (:int_flt_type=4) and (su.staff_id=:int_id1 or su.staff_id in (select id from SYS$GET_STAFF_LIST(:int_id1,null,null))) ) or
            ( (:int_flt_type=5) and (su.dept_id=:int_id1) ) or
            ( (:int_flt_type=6) and (su.club_id=:int_id1 and su.dept_id=:int_id2) )
           )      
      into ...
процедура в зависимости от входящих параметров - :int_flt_type и :int_id1 - возвращает разные наборы данных, отфильтрованные по этим параметрам.

раньше я думал, что IB выполняет условия, как любой нормальный язык программирования или скрипт, т.е. например в строке

Код: Выделить всё

( (:int_flt_type=1) and (su.id=:int_id1) )
проверится первое условие, и, если оно TRUE, то проверится второе, а если FALSE - второе не будет даже проверяться.

как показала практика - это не так. все условия проверяются в любом случае.
в результате если при выполнении процедуры(см. большой код выше) переменная int_flt_type будет равна 2, то все равно будут проверяться все условия в where, включая подзапрос из процедуры SYS$GET_STAFF_LIST.
А это, как Вы понимаете, тормоза на пустом месте, ибо если int_flt_type не равна 4, подзапрос(и почти половину других условий) не нужно даже трогать.

собственно вопрос - почему так и можно ли с этим что-то сделать?

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 13 ноя 2006, 13:22

Если индексы для таких проверок не используются, и тебе уж так хочется "оптимизации", то выбирай всё, а затем вложенными IF'ами проверяй.
Я такой способ применяю, когда проверка подключает функции или ещё что-нибудь трудозатратное, типа операций со строкой.

Для простых числовых проверок, как ты привёл, твоя возня с оптимизацией яйца выеденного не стоит. Тестирование обоих вариантов покажет тебе, что потери времени пренебрежительно малы.

Повторюсь - если не используются индексы. Иначе картина может разительно меняться.

dsd_corp
Сообщения: 8
Зарегистрирован: 12 ноя 2006, 15:32

Сообщение dsd_corp » 13 ноя 2006, 15:35

Индексы используются. меня как раз и не волнуют простые числовые проверки - там действительно разница в производительности пренебрежительно мала. Меня волнует подзапрос - в нем выгребается дерево.

если проверки перенести в тело цикла, будет еще хуже - дергать подзапрос на каждую запись мастер-таблицы.


а если совсем точно высказаться - меня волнует, можно ли как-то сделать, чтобы подзапрос выполнялся только при наличии определенного условия.

CyberMax
Заслуженный разработчик
Сообщения: 638
Зарегистрирован: 31 янв 2006, 09:05

Сообщение CyberMax » 13 ноя 2006, 17:18

Полное вычисление логического значения было в FB 1.0. "Ускоренное" вычисление реализовано в FB 1.5. Вывод: борландовцы, скорее всего, это просто не реализовали в семерке. Либо это где-то в конфиге настраивается.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 13 ноя 2006, 18:17

Кстати, за писанину "su.staff_id in (select id from...", я бы заставил читать доку по SQL.
Либо exists, либо ...
Хотя, такому условию, как ты написал, почему-то мне кажется, что индексы не помогут. Оформить бы типа:

Код: Выделить всё

declare variable ok smallint; /* Переменная для проверки условия */
for select 
  ...
  from staff_units su /* без where! */
  into id, pers_id, club_id, staff_id, dept_id, ...
do begin
  ok=0;
  if (int_id1=-1) then ok=1;
  else
  if ((int_flt_type=1) and (int_id1=id)) then ok=1;
  else
  if ((int_flt_type=2) and (int_id1=pers_id)) then ok=1;
  else
  if ((int_flt_type=3) and (int_id1=club_id)) then ok=1;
  else
  if (int_flt_type=4) then begin
    if ((int_id1=staff_id) or exists (select 1 from SYS$GET_STAFF_LIST (:int_id1, null, null) where id=staff_id)) then ok=1;
  end else
  if ((int_flt_type=5) and (int_id1=dept_id)) then ok=1;
  else
  if ((int_flt_type=6) and (int_id1=club_id) and (int_id2=dept_id)) then ok=1;

  if (ok=1) then begin
    ...
  end
end

dsd_corp
Сообщения: 8
Зарегистрирован: 12 ноя 2006, 15:32

Сообщение dsd_corp » 14 ноя 2006, 13:32

WildSery писал(а):Кстати, за писанину "su.staff_id in (select id from...", я бы заставил читать доку по SQL.
Либо exists, либо ...
Хотя, такому условию, как ты написал, почему-то мне кажется, что индексы не помогут. Оформить бы типа:
...
т.е. твоя писанина по-твоему быстрей будет? таким образом ты заставил дергать процедуру SYS$GET_STAFF_LIST на каждую запись из staff_units. Где здесь оптимизация? раскройте мне глаза...

в моем случае я по крайней мере давал шанс оптимизатору закешировать результаты выполнения SYS$GET_STAFF_LIST, дернув ее один раз

или я не прав?

и еще. если оформить по-твоему, как в таком случае помогут индексы?
имхо никак, ибо из staff_units все-равно читаться будут все строки(кортежи). в моем же оформлении они хотябы не все "фетчатся" в переменные.

P.S. это не наезд, я просто хочу понять, где истина... :)

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 14 ноя 2006, 15:01

Зависит от того, сколько данных выбирается таким запросом.
Насчёт кэширования запроса - не уверен, что он у тебя кэшируется. Надо руками щупать.
То, что я написал - просто свободное теоретизирование. Чтобы построить оптимальный алгоритм, надо знать данные.

Приведи план твоего запроса.

Ответить