Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Sybase ASA, ASE, IQ [игнор отключен] [закрыт для гостей] / Использование функций в запросах / 5 сообщений из 5, страница 1 из 1
22.01.2004, 22:08
    #32385349
Aleksey Kh.
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование функций в запросах
В продолжении темы начатой тут.\r
\r
Есть запрос, использующий функцию:\r
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
\r
select i.dOut, Invoice_Sum(i.id_nto,i.pr_invoice,i.id_invoice,null, 2 ) as inv_sum,i.id_currency\r
from t_invoice as i \r
where i.id_nto=\'Sang\'\r
  and i.id_client= 3 \r
  and i.place=\'C\'\r
  and (i.dfOpl>=\'2004-01-01\' or (i.dfOpl is null))\r
  and i.dOut<=\'2004-01-31\'\r
  and inv_sum> 1000 
\r
\r
Вот от него план:\r
Код: plaintext
1.
2.
3.
4.
\r
( Plan [ Total Cost Estimate: . 2680847042  ] \r
  ( IndexScan ( t_invoice i ) idx_clients[ ( ( i.DfOpl IS NULL :  6 . 836465001 % Statistics ) OR ( i.DfOpl >=   2004 - 01 - 01  :  5 . 816499708 % Statistics ) :  12 . 65296471 % Statistics ) AND ( i.Place =  \'C\' :  98 . 3531177 % Statistics ) AND ( i.DOut <=   2004 - 01 - 31  :  100 % Statistics ) AND (  "branch" . "Invoice_Sum" ( i.id_nTo, i.pr_invoice, i.id_invoice,  NULL,   2  ) >   1000  :  25 % Guess ) ] )\r
)\r
\r
\r
Насколько я понял, сначала записи были отобраны по условиям (id_client=3) и только потом была посчитана функция. \r
Чем, в таком случае, грозит использование функций?\r
\r
p.s. ASA9
...
Рейтинг: 0 / 0
22.01.2004, 22:44
    #32385373
ASCRUS
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование функций в запросах
Здесь ничем :) Действительно функция будет вызвана только на полученные отфильтрованные записи.

Однако например, если в функцию вставить какой нибудь SQL запрос, то мы получим вызов этого запроса столько раз, сколько получилось строк после фильтрации основного запроса. В плане запроса естественно такой "подзапрос" обработаться и отобразиться не может, поэтому скорее всего такая уже функция будет тормозить выполнение запроса тем, что постоянно генерить еще запросы, на которые оптимизатору тоже надо строить план запроса и не факт, что он удачно осядет в кэше, плюс на время выполнения таких вот запросиков будут идти постоянные блокировки/разблокировки и т.д. Работы как говориться будет делаться много.

Далее еще важный пример : обычная скалярная функция, пусть даже туда параметром передается константа, но участвует такая функция в групповом фильтре:
Код: plaintext
1.
2.
3.
4.
5.
select Id, Sum(Value)
from Table
group by Id
having Id = MyFunc(  1  )  -- Пусть например функция возвращает 100
 

Здесь сначала будут посчитаны суммы всех групп Id, а потом проведена фильтрация по Id = 100, что не очень приятно и приводит к Table Scan таблицы.

Следующий скрипт на пару строчек больше, но выполняется в разы быстрее:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
declare @Id int;
set @Id = MyFunc(  1  );  -- Возвратит к примеру 100
 

select Id, Sum(Value)
from Table
group by Id
having Id = @Id;

По плану здесь сначала будете применен фильтр по Id = 100, в том числе по индексу, если Id проиндексированно. Далее уже по полученым записям будут проведены группировки и аггрегирование.

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

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

Вот вроде и все мысли по поводу UDF :)
...
Рейтинг: 0 / 0
23.01.2004, 10:45
    #32385749
Aleksey Kh.
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование функций в запросах
ASCRUS...В плане запроса естественно такой "подзапрос" обработаться и отобразиться не может, поэтому скорее всего такая уже функция будет тормозить выполнение запроса тем, что постоянно генерить еще запросы, на которые оптимизатору тоже надо строить план запроса и не факт, что он удачно осядет в кэше...

часто приходится использовать конструкции:
Код: plaintext
1.
2.
3.
4.
select id_Х, ...,
  (if условие1 then ... else ... endif)
where ...
  and id_Х in (if условие2 then (select ...) else (...)  endif) 


Разобраться потом (спустя полгода, например) в таком запросе невозможно.
К тому же, насколько я понимаю, с (if условие2 then (select ...) else (...) endif) у оптимизатора могут возникнуть те же сложности, что и с функциями.
Начал пользоваться фунциями, пока считаю такой подход оправданным.

Есть еще одна проблема: есть шапка счета (t_invoice), есть содержание (t_order).
Понятно, что постоянно нужна сумма счета.
Как быть? Сейчас везде считается подзапросом.
Очень хочется иметь сумму без вычислений.
Можно в триггере, но иногда необходимо править t_order без триггеров.
Плюс, у нас распределенная база (SQL Remote) и без триггеров могут работать админы в филиалах, а сумму хотелось бы иметь корректную.
Как быть?
...
Рейтинг: 0 / 0
23.01.2004, 11:51
    #32385925
ASCRUS
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование функций в запросах
Код: plaintext
1.
2.
3.
select id_Х, ...,
  (if условие1 then ... else ... endif)
where ...
  and id_Х in (if условие2 then (select ...) else (...)  endif) 

Первый if ничего страшного не делает - это обычное вычислямое поле в запросе. А вот со вторым if Вы конечно погорячились. Если посмотреть план запросов, то будет организовано 2 subquery и они оба будут выполняться, а полученный результат участвовать в фильтре в зависимости от Условия2. Вот такие вещи я и не рекомендую делать - фактически Вы режите работу оптимизатора на корню, на малых обьемах этого не будет заметно, а вот если в таблице миллионы записей, то долгое ожидание выполнения запроса гарантированно. Если у Вас Условие2 статично, то гораздо легче написать вот так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
declare @Result int;
if Условие2 then
  select ...
  into @Result;
else
  select ...
  into @Result;
end if;

select id_Х, ...,
  (if условие1 then ... else ... endif)
where ...
  and id_Х = @Result;

План такого запроса будет примитивным и быстрым. Если же у Вас в Условие2 участвуют поля, то однозначно такой запрос вообще надо переписывать по другому или же задуматься об правильности организации структуры БД. Как один из вариантов более легкой для оптимизатора версии запроса:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
select id_Х, ...,
  (if условие1 then ... else ... endif)
where ...
  and Условие2 
  and id_Х in (select ...)  -- select при выполнении условия2
 
union all
select id_Х, ...,
  (if условие1 then ... else ... endif)
where ...
  and not Условие2 
  and id_Х in (select ...)  -- select при невыполнении условия2
 

Здесь план запроса будет приличным, никаких Subquery и при наличие нужных индексов оптимизатор сможет ими нормально воспользоваться. Я рекомендую себе взять за правило, что конструкция такого типа:
Код: plaintext
1.
2.
select *
from Table
where Field in (select ... from Table1 ) or Field in (select ... from Table2)

будет всегда медленней работать, чем:
Код: plaintext
1.
2.
3.
4.
5.
6.
select *
from Table
  inner join Table1
union all
select *
from Table
  inner join Table2

фильтрация по наборам данных с помощью OR всегда медленнее, чем с помощью UNION ALL, и IN во многих случаях может оказаться медленнее, чем INNER JOIN, особенно на больших обьемах данных.

авторЕсть еще одна проблема: есть шапка счета (t_invoice), есть содержание (t_order).
Понятно, что постоянно нужна сумма счета.
Как быть? Сейчас везде считается подзапросом.
Очень хочется иметь сумму без вычислений.
Можно в триггере, но иногда необходимо править t_order без триггеров.
Плюс, у нас распределенная база (SQL Remote) и без триггеров могут работать админы в филиалах, а сумму хотелось бы иметь корректную.
Как быть?
Если записей в содержании на запись в шапке счета мало, то легче действительно просто считать, скорость при этом не потеряется. Если записей много, то все действительно лучше через триггера - делаете поле TotalSum в t_invoice и при любом добавлении/изменении/удалении записи из t_order просто ее пересчитываете в after триггере на for each statement. Я правда не понял, почему Вы хотите править t_order с отключенными триггерами, но всегда можно сделать глобальную процедуру пересчета итоговых сумм, которая заново рассчитает все итоговые суммы в t_invoice, если они не совпадают с t_order.

Как видите из любой ситуации может быть много выходов. Но в РСУБД лучше помнить, что самый красивый по коду и легкий по реализации выход может оказаться на самом деле с точки зрения РСУБД самым кривым и тормознутым. Я бы Вам порекомендовал не пытаться облегчить жизнь себе, как программисту, а в первую очередь думать о самой СУБД, чтобы было удобно ей. Иначе нарветесь на кучу неприятностей. На это я насмотрелся предостаточно, консультируя людей по их БД, которые при больших обьемах данных просто останавливались, повергая их разработчиков в шок, как оказывается они ошибались, используя навороченные запросы, повторно используемый код, оформленный в виде ХП и UDF, структуру таблиц в виде деревьев и т.д. Причем пока данных было мало все работало как часы - быстро и красиво.
...
Рейтинг: 0 / 0
27.01.2004, 09:01
    #32388547
Aleksey Kh.
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование функций в запросах
ок.
буду думать.
...
Рейтинг: 0 / 0
Форумы / Sybase ASA, ASE, IQ [игнор отключен] [закрыт для гостей] / Использование функций в запросах / 5 сообщений из 5, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]