Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Функция в функции в функции...  [new]
Guf
Member

Откуда: Новосибирск
Сообщений: 663
Исходные данные:
В начале было слово требовалось найти предыдущий рабочий день от заданной даты.
Не вопрос, пишем функцию:
CREATE FUNCTION [dbo].[GetPrewWorkDay](@Date DATE)
RETURNS DATE
AS
BEGIN
----- function text
RETURN @PrewWorkDay
END;
GO

Сейчас не рассматриваются особенности реализации, скалярная/табличная, способ поиска предыдущего и т.д. Вопрос состоит в другом.

Далее в некоторых местах требуется возвращать текущую дату, если она является рабочим днем, иначе возвращать предыдущий рабочий день.
Подняв на знамена принципы удобства сопровождения, пишется такое:
CREATE FUNCTION [dbo].[GetPrewWorkDayIncudeCurrent](@Date DATE, @IncludeCurrent BIT)
RETURNS DATE
AS
BEGIN
DECLARE @PrewORCurrentWorkDay DATE
----- function text
RETURN @PrewORCurrentWorkDay
END;
GO

ALTER FUNCTION [dbo].[GetPrewWorkDay](@Date DATE)
RETURNS DATE
AS
BEGIN
RETURN [dbo].[GetPrewWorkDayIncudeCurrent](@Date, 0)
END;
GO

Т.е. чтобы не менять всё ранее написанное, создается другая функция удовлетворяющая ТЗ. А существующая вызывает новую с параметром "по умолчанию".

Прошло время, бизнесу захотелось отсчитывать N дней назад. И тут опять...
CREATE FUNCTION [dbo].[GetPrew_N_WorkDayIncudeCurrent](@Date DATE, @N INT, @IncludeCurrent BIT)
RETURNS DATE
AS
BEGIN
DECLARE @Prew_N_ORCurrentWorkDay DATE
----- function text
RETURN @Prew_N_ORCurrentWorkDay
END;
GO


ALTER FUNCTION [dbo].[GetPrewWorkDayIncudeCurrent](@Date DATE, @IncludeCurrent BIT)
RETURNS DATE
AS
BEGIN
RETURN [dbo].[GetPrew_N_WorkDayIncudeCurrent](@Date, 1, @IncludeCurrent)
END;
GO


Вопросы:
1. Стоит так делать? И до каких пор?
Я не раз слышал, и на этом форуме в первую очередь, что вызов функции = накладные расходы, особенно скалярной.
Я так понимаю, что вызов функции, которая вызывает другую функцию, которая вызывает другую функцию - это значит превращать сервер в обогреватель...
2. Есть какие-нибудь рекомендации как поступать в подобных случаях?
3. Где та тонкая грань между оптимальным кодом и удобством сопровождения?
14 апр 15, 09:43    [17511163]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Glory
Member

Откуда:
Сообщений: 104751
Guf
Я не раз слышал, и на этом форуме в первую очередь, что вызов функции = накладные расходы, особенно скалярной.
Я так понимаю, что вызов функции, которая вызывает другую функцию, которая вызывает другую функцию - это значит превращать сервер в обогреватель...

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

Guf
3. Где та тонкая грань между оптимальным кодом и удобством сопровождения?

У каждого она своя. Вот вы сами "Подняв на знамена принципы удобства сопровождения" написали новую функцию. Хотя могли переписать существующую. И под тем же предлогом.
14 апр 15, 09:54    [17511212]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Guf
Member

Откуда: Новосибирск
Сообщений: 663
Glory
Все это относится к использованию функции в каком то запросе для каждой записи.
Для одиночного вызова накладные расходы малы для человечского восприятия. Хотя с ними тоже борятся.

И так и так бывает.
Glory
У каждого она своя. Вот вы сами "Подняв на знамена принципы удобства сопровождения" написали новую функцию. Хотя могли переписать существующую. И под тем же предлогом.

Написал я её, конечно, сам, но не по верению сердца, а по требованию руководства.

Случай не единичный, но самый простой для изложения и самый показательный.
Аргументы о том, что таже фунция "раскрытая" и перенесенная в джоин, делает то же самоев 50 раз быстрее. Разбиваются об 50мс vs. 500мс одинаковы с точки зрения пользователя, а поддержка лапше-кода гораздо более затратна для программистов.
Но в некоторых местах эта разница накапливается и 10 * (50мс vs. 500мс) дает 500мс vs. 5с

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

Многие авторы ссылаются на некую "золотую середину", но вот как её найти, не говорят.
14 апр 15, 10:18    [17511307]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Glory
Member

Откуда:
Сообщений: 104751
Guf
Написал я её, конечно, сам, но не по верению сердца, а по требованию руководства.

Требования - это и есть принципы построения вашей системы. Вы хотите их обсудить ? Или вы хотите услышать от других про требования в их системах ?

Guf
Наверное, некого набора рекомндаций (ссылку на статью или исследования), который не нужно применять бездумно, но который даст некую точку опоры.

"Как научиться правильно программировать за 24 часа" ?
14 апр 15, 10:23    [17511330]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Guf
Member

Откуда: Новосибирск
Сообщений: 663
Glory,

14 апр 15, 10:47    [17511451]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
alexeyvg
Member

Откуда: Moscow
Сообщений: 32174
Guf
Я, по сути, не могу сформулировать, чего именно я жду от сообщества. Наверное, некого набора рекомндаций (ссылку на статью или исследования), который не нужно применять бездумно, но который даст некую точку опоры. В таких условиях провести такие и сякие тесты, и если результаты в этом диапозоне, то делать так, иначе подумать и найти другой способ.
Вот, собственно, Glory сформулировал некое правило: "не вызывать функцию для каждой записи, а вызывать только для записей в результатирующем наборе".

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

Поэтому предпочтительно подойти с другой стороны - не использовать скалярные функции, заменяя их на табличные (особенно inline), на проектирование правильной модели данных, на перенос кода в процедуры.

Вот, наверное, такой подход есть правильный.
Guf
В таких условиях провести такие и сякие тесты
Да тест даст вполне предсказуемый результат: если скалярная функция будет вызываться часто, то она очень сильно всё замедлит. Если при этом функции вложенные, то прибавится ещё небольшое замедление, в несколько раз.
Эти "несколько раз" действительно "немного", по сравнению с замедлением в сотни раз от использования скалярок вообще.
14 апр 15, 11:05    [17511568]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Jaffar
Member

Откуда:
Сообщений: 636
alexeyvg,

На мой взгляд основная проблема тут в том чтобы сделать выбор между:

1.тем чтобы все сделать "правильно" т.е. сделать 1 новую функцию с 3 параметрами и заменить все устаревшие варианты функции во всех местах, устаревшие удалить и далее использовать только новую функцию

и

2. тем что 1 вариант занимает много времени и поэтому нужно сделать "как быстрее" сейчас, а гемморой, что в системе есть 100500 функций которые х/з как работают и делают примерно одно и тоже, вызываются друг из друга и чтобы докопаться до истины и разобраться в вопросе(если например где-то косяк) нужно развернуть всю эту портянку в 20 вызовов и сидеть втыкать что-же там написано....
Особенно это достает когда дело касается вьюх и табличных функций которые друг из друга вызываются, да еще и со всякими группировками, переименованием столбцов и case`ами и т.п. и пока дойдешь до таблицы проклянешь того кто это делал.
т.е. во втором варианте мы откладываем гемморой "на потом", скапливая его в темном чулане.

Это приводит к тому что по мере роста "чулана" даже небольшие сравнительно косяки начинают требовать перетряхивания этого темного чулана и превращаются в каторгу ибо нет работы хуже чем разбираться в чужом коде написанном по 2 варианту.

НО это философия.

Конкретно по вашей функции есть решение, так сказать "между струйками":
сделать у 1 функции параметр varchar(255) и в нем через запятую перечислять аргументы типа '2014-05-01,1, 24'
а внутри функции бить этот 1 параметр на 3 или на сколько надо.

Хотя конечно в идеале нужно было бы спрогнозировать задачу и решением покрыть все возможные потребности, но это в идеале.
14 апр 15, 12:05    [17511987]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Guf
Member

Откуда: Новосибирск
Сообщений: 663
alexeyvg
Вот, собственно, Glory сформулировал некое правило: "не вызывать функцию для каждой записи, а вызывать только для записей в результатирующем наборе".

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

Поэтому предпочтительно подойти с другой стороны - не использовать скалярные функции, заменяя их на табличные (особенно inline), на проектирование правильной модели данных, на перенос кода в процедуры.

Я уже 7 лет работаю в одной компании над поддержкой купленного 15 лет назад коробочного продукта с большой свободой для настройки/доделки/переделки (не 1С).
Есть модель данных.
Тонкий клиент.
Вся бизнес логика в хранимых процедурах _и только в них_ (добавление/изменение/удаление + обсчеты и отчеты).
Нет ограничений, кроме первичного ключа (их проверка в процедурах).
Нет внешних ключей (их проверка в процедурах).
Функций нет.

Изначально ядро системы писалось под SQL Server / Sybase и разработчики заложили в систему этакую "свинью". Есть свой метаязык (он то и позволяет в 2 строки кода проверить все ограничения и вставлять/изменять/удалять данные в базу), который интерпретатор преобразовывает в скл.
Отсюда при поступлении информации из вне нужно писать курсор, который вызовет процедуру создания документа, а потом его подтверждения, т.е. обсчета и создания зависимых документов, и их подтверждения - круг замкнулся. Я встречал до 4! вложенных курсоров там, где можно было обойтись четырьмя инсертами.
Этот проклятый интерпретатор не знает про джоины! И перечисляет таблицы в кляузе FROM через запятую, а в WHERE соединяет их при помощи *=. По этому уровень совместимости базы 80 и дальше 2008R2 ни-ни. А также забудь про табличные инлайн функции.
Но можно писать сразу процедуры сразу на T-SQL, интерпретатор его передает его сиквелу как есть. И последние 3-4 года мы стараемся что-то как-то изменить, улучшить, использовать (скалярные) функции , там где считаем разумным. В особо критичных к производительности местах используем процедуры с TVP и без курсоров и т.д.
И на форуме я действительно, как верно подметил Glory, хотел услышать о других подходах. Потому что кроме этой системы я ничего другого и не видел. У меня есть свои взгляды на систему, принципы разработки и т.п. И если мне кто-то расскажет о других подходах, я смогу их осмыслить и постараться обосновано применить в своей работе.

P.S. Как-то текст выше смахивает на плач в жилетку. Я не то имел в виду. Я хотел рассказать о причинах моего интереса к данной теме и тех целях, которые преследую, задавая вопрос.
14 апр 15, 12:35    [17512177]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Guf
Member

Откуда: Новосибирск
Сообщений: 663
Jaffar,

+100500

Уже реализован вариант 2. Я изо всех сил буду ратовать за вариант 1. И "я костьми лягу, но не сдамся", если меня попросят реализовть ваш вариант №3
14 апр 15, 12:38    [17512227]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Guf
Member

Откуда: Новосибирск
Сообщений: 663
Guf
...И если мне кто-то расскажет о других подходах...

Понимаю, что прошу через чур много. Поэтому и прошу кинуть в меня ссылкой, если кто-то что-то видел по данной теме. Google меня совсем не понимает. И показывает не то что я хочу. Наверное я не так спрашиваю.
14 апр 15, 12:49    [17512318]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 9169
Guf, Вы мыслите не реляционно. Сиквел не любит "матрёшек", "наследований" и прочих достижений гибкого ума.
Сиквел любит запросы и только запросы.
14 апр 15, 13:45    [17512752]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
alexeyvg
Member

Откуда: Moscow
Сообщений: 32174
Guf
И на форуме я действительно, как верно подметил Glory, хотел услышать о других подходах. Потому что кроме этой системы я ничего другого и не видел. У меня есть свои взгляды на систему, принципы разработки и т.п. И если мне кто-то расскажет о других подходах, я смогу их осмыслить и постараться обосновано применить в своей работе.
К сожалению, MS несколько запустил T-SQL, то направление, которое называется Programmability.
Трудно обеспечить повторное использование кода, трудно делить на модули, ну и так далее. Нет пока даже уровня 80-х, не говоря уже о 21 веке.
Это вызвано, с одной стороны, (ИМХО) слабостью этого направления в MS, с другой, стратегией "переноса логики из сиквела наружу".

Приходится приспосабливаться.

Делать повторное использование кода на том что есть - не получается. Будет в итоге совершенно неработоспособная вещь. Так что оставьте надежды, не используйте все эти функции.

Первое - нужно работать с моделью данных, думать в первую очередь в этом направлении.
Например, ваши GetPrewWorkDayIncudeCurrent и GetPrewWorkDay прекрасно реализуются в виде таблиц(ы), что и правильнее для РСУБД. То есть, например, богатство PL\SQL играет плохую роль - разработчики думают императивно, в терминах классических ЯП, делают построчную обработку и циклы, вместо правильной модели данных и запросов.

Второе направление - компенсируйте убожество языка нормальными средствами разработки. Допустим, в нормальном проекте заменить все вызовы функции было бы совсем несложно. Потом собрать билд, накатить на тестовый сервер, потом накатить на продакшен. Билды, версионный кконтроль, массовый поиск и замена, привязка изменений к бизенс-задачам - всё это резко облегчает работу с большими проектами.
Очень неплохи опыты с использованием макро - там ваши проблемы с формулами решилиь бы легко и приятно; впрочем, широкого распространения этот подход не получил.

Третье - использовать то, что есть, для структурирования и повторного использования кода. OUTPUT, TVP, InMemory таблицы, передача множеств через XML. В общем, пытаться писать правильно, но без кривых неработающих средств, к каковым относятся скалярные функции.
И всегда, всегда строго следить, что бы не было вороха скалярного кода! Только работа с множествами, везде!
14 апр 15, 15:25    [17513409]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 9169
Думаю, что это сделано намеренно, дабы получить "чистый" SQL язык, лишенный "процедурности".
14 апр 15, 16:56    [17514155]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Lepsik
Member

Откуда: glubinka
Сообщений: 4257
Владислав Колосов
Guf, Вы мыслите не реляционно. Сиквел не любит "матрёшек", "наследований" и прочих достижений гибкого ума.
Сиквел любит запросы и только запросы.


к сожалению вы мыслите слишком широкими категориями. У меня вот есть полсотни репортов (многие из них идиологически похожи) и мне некоторых идей из ООП очень не хватает.

Частично обхожусь табличными перемеными и пользовательскими типами если предпологаю что данных в ней не превысит 50K записей
14 апр 15, 19:00    [17514648]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
Lepsik
Member

Откуда: glubinka
Сообщений: 4257
Lepsik,

Кстати банальный include был бы настояшим прорывом.
Можно было делать реструкуризацию через раскидыванием однообразных кусков кода в файлы.

В принципе если сделать препроцессор по типу C++ можно было многое упростить.
14 апр 15, 21:27    [17515217]     Ответить | Цитировать Сообщить модератору
 Re: Функция в функции в функции...  [new]
alexeyvg
Member

Откуда: Moscow
Сообщений: 32174
Lepsik
Lepsik,

Кстати банальный include был бы настояшим прорывом.
Можно было делать реструкуризацию через раскидыванием однообразных кусков кода в файлы.

В принципе если сделать препроцессор по типу C++ можно было многое упростить.
Да, вот я про это и писал.
При билде прогоняли код через С-шний прекомпайлер, согответственно было удобно писать всякие выражения, константы, определения временных таблиц (для совместного испрользования процедурами) и т.д.

Тут естественно важнее не включения кода, а макросы.
14 апр 15, 22:06    [17515366]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить