Как стать автором
Обновить

Комментарии 104

Обсуждалось 4 года назад

«В карантин нагрузка выросла в 5 раз, но мы были готовы». Как Lingualeo переехал на PostgreSQL с 23 млн юзеров https://habr.com/p/515530/

А, это где еще все поувольнялись из-за такой идеи?))

SQL — это язык для агрегации данных, а не язык для описания бизнес-логики приложения

Дальше читать не имеет смысла

https://en.wikipedia.org/wiki/SQL
"The scope of SQL includes data query, data manipulation, data definition, and data access control."

Но хранимки пишут не на sql. В каждой СУБД обычно какой-то свой, и часто даже не один, язык для написания хранимых процедур.

Но процитировано-то утверждение со словом SQL.

Это точно.

Насчёт тестирования. Автор наверное забыл упомянуть про возможность тестов на реальной СУБД? Я не то, чтобы фанат хранимок, но надо как-то реальные недостатки обсуждать, а не проблемы возникающие от незнания/неумения.

Проблемы, описанные в разделах "Сложность в тестировании" и "Автоматическое тестирование или unit-тесты" в значительно мере могут решены связкой test-containers + [ваш любимый инструмент миграции] (для распространённых СУБД всю конструкцию можно запустить за день с нуля). В этом случае, интеграционный тест на хранимку собственно и будет её актуальной документацией.

Автор наверное забыл упомянуть про возможность тестов на реальной СУБД?

Это интеграционные тесты, а он говорит про юнит-тесты.

могут быть решены связкой test-containers + [ваш любимый инструмент миграции]

Он же не сказал, что они не могут быть решены, он говорит, что это сложнее. В этом я с ним согласен.

Хм, а в чем проблема в unit-тестах для хранимых процедур?
Открываем транзакцию, заполняем фикстурой (тривиальный insert из таблицы с тестовыми данными), вызываем процедуру, проверяем утверждения, откатываем транзакцию.
Это довольно просто пишется, быстро работает, легко модифицируется.

В том, что это не юнит-тест. Это интеграционный тест с вводом-выводом по сети и на диск. Юнит-тест в классическом понимании так не делает.

С транзакцией тоже есть вопросы. Вот допустим у меня есть бизнес-требования "Сохранить объект в базу с одним статусом, отправить по сети в другую систему, при успешной отправке ставить другой статус, при ошибке ставить третий". Здесь должно быть 2 транзакции, до и после отправки по сети. Как написать тест на эту логику? В приложении я могу замокать базу и клиент к другой системе, и проверить, какие данные будут через них отправляться, без фактической отправки.

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

Во-первых, откуда в указанном примере сеть и диск? Сам тест пишется на sql, выполняется внутри БД и на диск ничего не пишет.
Во-вторых, что за "классическое понимание"? Единица тестирования - хранимая процедура, ее и тестируем, в чем проблема?
В-третьих, то, что описано как "бизнес-требование" - таковым не является, описана техническая реализация. Нужно ли именно такое - не понятно, тем более в системе уровня "логика в БД".
Ну и так далее....

Во-первых, откуда в указанном примере сеть и диск?

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

Во-вторых, что за "классическое понимание"?

https://en.wikipedia.org/wiki/Unit_testing
Unit testing, a.k.a. component or module testing, is a form of software testing by which isolated source code is tested to validate expected behavior.
One goal of unit testing is to isolate each part of the program and show that the individual parts are correct. A unit test provides a strict, written contract that the piece of code must satisfy.

https://www.baeldung.com/cs/unit-testing-vs-tdd
Isolated. A unit test shouldn’t modify or depend on any external state.

В-третьих, то, что описано как "бизнес-требование" - таковым не является

Именно является. Аналитики от бизнеса знают, что такое БД и сохранение данных. И хотят сначала сохранить в нашу базу, а потом отправлять в другую систему. Иначе может быть так, что в другой системе запись создалась, а в нашей после этого произошла ошибка транзакции, и данные не сохранились. Это именно бизнес-логика, такая же как "После создания заказа отправить email пользователю".

[о да, религиозные фанатики в интернете...]

В сторону от темы: Вы же понимаете, что все эти требования, про то, что юнит-тест должен, а чего не должен, не обоснованы ничем (я, естественно, читал все эти теоретические обоснования, и говорю это на основе прочитанного), кто-то верит, что оно должно быть так, как в википедии написано, кто-то нет. Это не особо важно - разработка это ремесло, а не теория. Есть распространённые примеры других подходов, например в Рельсах тесты на реальной БД идут из коробки, и называются юнит-тестами (работает в точности по описанной выше схеме).

По теме: Мы говорим не об абстрактном коде, а именно о коде взаимодействующем с базой данных. Этот код почти всегда включает запросы к БД (ORM тоже, просто вы их не видите). Т.е. у нас есть, как-бы isolated source code, который включает инструкции к внешней системе, которые только эта внешняя система и может интерпретировать. Это точно isolated? Печальный факт состоит в том, что на моках базы данных вы никак не проверите, что внутри этих запросов написано (нестрого говоря, тесты репозитория на моках - это погружение в мир иллюзий, у вас как-бы 100% покрытие, но при этом всё равно deploy-and-pray стиль интеграции). И если раньше (давно) тестирования кода на реальной базе данных было сопряжено с некоторыми техническим трудностями, то сейчас контейнеризация решила большую часть этих проблем. Технически, у вас всё равно есть какой-то setup метод, который выполняется до тестов, и большая ли разница, общие моки там настраиваются, или контейнер в память поднимается.

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

И по основному тезису - если у вас есть тесты на реальной БД (называйте их как угодно), то тестирование хранимок ничем не отличается от тестирования запросов. Если таких тестов у вас нет... ну, не знаю, наверное есть отрасли где данные ничего не стоят, сам не видел, врать не буду.

Вы же понимаете, что все эти требования, про то, что юнит-тест должен, а чего не должен, не обоснованы ничем

Был вопрос "Что за классическое понимание?", я привел ответ с пруфами. Неважно, чем они обоснованы, это все равно является ответом на этот вопрос.

Т.е. у нас есть, как-бы isolated source code, который включает инструкции к внешней системе, которые только эта внешняя система и может интерпретировать. Это точно isolated?

Source code сам по себе не является isolated, он явлется isolated только в тестах. Тесты настраиваются таким образом, чтобы сделать код юнита isolated. Это написано в приведенном тексте: "goal of unit testing is to isolate".

Печальный факт состоит в том, что на моках базы данных вы никак не проверите, что внутри этих запросов написано

Вот разговор как раз о том, что для теста юнита это совершенно неважно. В юнит-тесте мы проверяем только поведение самого юнита на заданных данных, а не всех его нижележащих компонентов. На эти компоненты есть свои юнит-тесты.

Ну, таблица для тестов может и в памяти жить, в чем проблема? Если для тестирования используются искусственные данные (а в юнит-тестах обычно так и есть), то все данные будут в памяти и так. Транзакция до завершения может и пишет в журнал, но его тоже можно развернуть в памяти, делов-то.
Все это достаточно просто решаемые вопросы, причем решать их умели еще лет 20 назад.

И нет, если у вас бизнес (не аналитики, а именно бизнес) знает про БД, то тут какие-то проблемы. Впрочем, в указанном примере вообще нет (и не может быть) решения, только eventually consistency, саги и так далее. Где и как будет реализована сага - бизнесу вообще без разницы (

Ну, таблица для тестов может и в памяти жить, в чем проблема?

Ну а как тестовые данные туда попадают? Какая-то программа подключается по сети, читает их откуда-то с диска и добавляет.

Все это достаточно просто решаемые вопросы

Ну а с юнит-тестами в приложении их вообще решать не надо. Это и показывает, что такие тесты в базе отличаются от юнит-тестов в приложении.
Смысл вообще не в том, что данные должны быть в памяти, а в том, чтобы тест не взаимодействовал с внешним для него состоянием или системами.

И нет, если у вас бизнес знает про БД, то тут какие-то проблемы.

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

Где и как будет реализована сага - бизнесу вообще без разницы

Я не сказал, что бизнесу есть разница, где и как реализована сага. У бизнеса есть требование "Сначала сохранить в нашей базе с этим статусом, потом отправлять в другую систему, по результату отправки обновлять статус". Тут нет требований где и как это реализовывать.

только eventually consistency, саги и так далее

Мне кажется, что в этом примере, сага будет некоторым перебором. Здесь, как мне кажется, вполне хватит обычного transactional outbox, если мы можем гарантировать идемпотентность по сообщениями на стороне системы получателя.

Совершенно правильные замечания с точки зрения казуального юзера, взаимодействующего только с верхними слоями БД. Конечно в отношении "сервис - база данных" не должны участвовать хранимки.
Однако не стоит забывать, что то, что вы видите от БД это лишь верхушка айсберга. Как известно на поверхности находится лишь 1/10 часть.
Само формирование базы не представимо и полностью невозможно без использования хранимок.
Если переводить в метафору - вы, в данном случае, владелец кредитного китайце в пробке, рассуждающий о том, что механическая коробка передач более не нужна и вообще морально устарела. А теперь представьте лицо профессионального гонщика (или Вин Дизеля), который бы это услышал. И вас понять можно, но и глупость, которую вы морозите, сложно оправдать. Не стоит обобщать весь мир АйТи под ваш опыт.

*Смайлик клоуна*

Встречал много проектов с базой без каких-либо хранимок. Так что представимо и возможно.

Встречал много проектов с хранимками, так что и это возможно тоже.

Я и не говорил, что проекты с хранимками невозможны.

мне, как .net разработчику, перешедшему в команду с достаточно большим количеством данных, прекрасно понятны мотивы, побудившие автара на написание сего опуса. Было бы все так просто, работоли бы все на одном "самом лучшем языке программирования". Если в приложении есть нормальный бэкэнд, очевидно - не стоит размазывать логику между ним и БД. Но в реальной жизни, не все ПО строится по трехзвенной архитектуре - есть целый пласт задач, где BLL на уровне СУБД суть рациональное решение.

А еще есть супер мега хайлоад известные на всю страну системы с бэкендом на делфи :)

Любой инструмент должен быть применен по назначению. В том числе и хранимые процедуры.

Текст процедур вообще ничем не отличается от текста на, к примеру, Java. Его можно прекрасно положить в GIT и так же версионировать. И точно так же документировать, точно так же тестировать на тестовой бд.

Единственный значимый аргумент - это привязка к конкретной СУБД. И то каждый сам для себя определяет допустимую степень риска. Кому было нужно - потратили время и деньги и перешли с oracle/mssql. Значит, задача не является нерешаемой.

Паническое нежелание работать с ХП иногда приводит к маразму. Настолько разработчики боятся малейшей логики в бд, что вместо select max(...), загружают список записей на клиента и уже там находят максимальное значение нужного поля (сам видел)

Настолько разработчики боятся малейшей логики в бд

Я видеть такой кейс(регулярно повторяемый)- backend выгружает весь набор строк и фильтрует на стороне приложения.

Закономерный итог -"а почему в psql запрос отработал миллисекунды а форма открывается минуту ?"

Ну все ж адекватно составленный SQL запрос (с max() вместо выгрузки всего и вся) - это не про хранение логики в БД.

Адекватно составленные оптимальные и быстрые SQL-ники тоже в коде проекта хранятся, просто в БД выполняются :)

Ключевое слово адекватный.

По ходу проведения импортозамещения с адекватными и профессиональными разработчиками большие проблемы .

Повторяюсь, сорри, но для них СУБД это хранилка данных. Как работает СУБД они не знают и знать не хотят .

В результате - "у нас проблемы с СУБД , очень много ожиданий Client read" , или "вы зачем используете эксклюзивные блокировки ? Это не мы у нас фреймворк такой" и т.д. и т.п. Ну а про то , какие запросы генерят ORM это отдельная тема . Я лично видел план запроса стоимостью триллион (10^12) и вопрос руководителя разрабов - "почему у нас медленно открывается форма "

Раньше было даже смешно и прикольно . Но когда происходит срыв сроков и постоянные аварии уже не смешно - "У нас проблемы с импортозамещением."

А с хранимками ситуация очень простая - современные разработчики просто не умеют писать бизнес логику в СУБД. Все наработки предыдущих поколений спустили в трубу в угоду моде и трендам.

На одном проекте, разработчики backend - в очереди стояли , набор вообще без проблем , на любой вкус и цвет. Разработчика СУБД искали год . Нашли с большим трудом.

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

Все просто .

современные разработчики просто не умеют писать бизнес логику в СУБД

Это наследие атлантов, великих колдунов древности. Смертные не могут в это чернокнижие....

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

Будь так, слали бы запрос с select max(...) и ХП не понадобилась бы. А тут больше похоже на то, что банально не подружились с генератором запросов в ORM.

Задача была именно такая: найти запись по сотруднику с максимальным score

Решение изумило: скачивали на клиента при каждом запросе (нагрузка была примерно 200 tps) около 20 строк, руками пробегали этот список и находили строку с максимальным значением, и брали все атрибуты именно с неё.

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

Бизнес-логика в базе это однозначно плохо. Банально затрудняет отладку и читаемость кода. Но иногда это вполне приемлемая цена за производительность. От задачи зависит.

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

Бизнес-логика в базе это однозначно плохо. Банально затрудняет отладку и читаемость кода.

Данное утверждение будет верно только при одном важном уточнении - читатель и отладчик не обладает знаниями Database Development .

Что и подтверждается следующим утверждением

Если разработчик шлепает код на отвали, он найдет где накосячить.

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

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

Ну это конечно неправильно в определенных ситуациях, но в других ситуациях это может быть более подходящим решением. Без контекста нельзя точно сказать. Кроме неумения использовать ORM, как сказали выше, могут быть например такие причины:
- Список записей потом используется для чего-то еще.
- Для произвольного запроса надо писать 20 строк, а для вычисления в приложении 1. Тот, кто советует отправлять max() в базу, поддерживать их конечно не собирается. При этом записей в этой выборке не больше 10.
- В коде проще отслеживать и менять логику средствами IDE, чем в строковых константах с SQL. Если разработчик приложения пропустит это место при изменениях требований, вы (DBA) будете ответственный или он? Вы будете исправлять этот баг в строковой константе? Зато 10 микросекунд сэкономили.

У нас архитектор вообще запрещает хранимки, триггеры и прочую логику в БД

Текст процедур вообще ничем не отличается от текста на, к примеру, Java.

Ну нет.

Во-первых, выразительность современных бекенд-языков (того же Java) на порядки лучше, чем у процедурных расширений SQL, используемых в современных СУБД. Попробуйте для сравнения написать более-менее крупную систему на Java и на PL/pgSQL, потом нам расскажите, что разницы никакой нет.

Во-вторых, инструментарий тоже очень разный. Например, для бекенда есть целая пачка крутых IDE, для СУБД я как не искал, не нашёл ничего похожего (чтобы и работа с Git, и тесты гонять, и отладка нормальная и т.д.). А ведь разработка не только в IDE упирается, и с экосистемой в СУБД всё значительно хуже.

Как-то попадалась мне на глаза "IDE для БД", то есть именно с полноценным дебаггером хранимых процедур, брейкпоинтами и остальным. Но она вроде стоила каких-то бешеных денег, и я даже пробовать ее не стал)

За большей частью текста полностью за !!! Работал 5 лет назад с оракловской бд, это дикая боль. За исключением как у них реализован рекурсивный обход, все остальное, жесть. Бизнес логика написана хранимками, и упиханна в пакеджи. Которые нужно ещё компилировать . Собирается все херово . Инстансы ещё нужно определенной версии. Но самая жопа это логика в базе!!!! База должна уметь репликации, дампить нормально, а не валится или битые с ходу делать. Я не хочу разбираться в сиквенсах, мне нужно надёжно данные сохранить . Уметь в кластер и давать АДЕКВАТНЫЕ ответы на ошибки. А не засирать диск тупыми логами , с нулевой информативностью . (Может в настройках дело, но это говно даже перезапустить боялись ) потому что она поднималась раза с 5 го. Логика - в код, данные в бд

При каждой пересборке пакетов, отлетают клиенты. И есть шанс уронить вообще все.

Согласен с автором логика в бд - за такое четвертовать !

Прочитав статью можно подумать, что хранимые процедуры были придуманы какими-то идиотами, у которых просто нормального ORM-а не было. И что у хранимок нет никаких плюсов, а сугубо одни минусы. А между тем плюсы всё-таки есть.

  • Хранимые процедуры заранее компилируются и оптимизируются движком SQL. И поэтому теоретически работают быстрее, чем просто запросы к базе, делающие ту же работу, что и процедуры. Хотя этот плюс несколько нивелирован современными движками БД, которые кэшируют SQL запросы и их результаты, в том числе параметризуемые запросы.

  • Лучший контроль за транзакциями и исключениями на низком уровне.

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

Хотя я лично и сам ненавижу, когда логика размазана по слоям, и что там не очень то ООП получается на уровне БД когда программируешь. Но не упоминать, что у процедур есть и плюсы - это странно.

Как вы так оптом, легко и просто, умножаете на ноль целую технологию.

У любой технологии есть свои «+» и «-», и для разных задач используют разные методики.

Способ хранения данных, доступ к ним определяется на этапе проектирования системы.  Даже типы серверов (MS SQL,  Oracle, MySQL, ..) отличаются по функционалу, и все это нужно учитывать.

Мне кажется, Ваше отношение к данной технологии вызвано неграмотно спроектированной системой на этапе бизнес логики, вызывающей такие проблемы. Должен быть уровень данных, предоставляющий данные в верхний уровень, и программист, пишущий, к примеру, на .NET, может совсем не знать что там внутри- SQL, XML или что-то совсем другое.

 

Тут, как в анекдоте:

-Вы не любите кошек? Да вы просто не умеете их готовить!

и программист, пишущий, к примеру, на .NET, может совсем не знать что там внутри- SQL, XML или что-то совсем другое.

Одни говорят, что автор не знает и не разобрался, другие, что он и знать не должен)
Кто будет писать в приложении этот "уровень данных, предоставляющий данные в верхний уровень" и потом поддерживать, вы (DBA) или разработчик приложения? Зачем ему эти сложности?

Хранимые процедуры замечательно тестируются, отлично оптимизируются. В задачах, для которых они предназначены, нет ни одного ЯВУ, который был бы более выразителен. Добавляют ещё один уровень безопасности, полностью скрывая реальные структуры данных, полиморфны, можно легко добавить трейсинг, профайлинг и т.п., прекрасно мигрируют на другую СУБД даже без ребилда и деплоя приложений, можно без труда реализовать поддержку множества СУБД, классно помогают в скейлинге/миграции схемы когда нужно осуществить плавную трансформацию в фоне петабайтов данных и временно спрятать её за вызовом хранимки. У нас замечательно и из Linq генерируются хранимки. А вот за что точно надо отрывать руки - это когда противники хранимок гоняют гигабайты данных туда-сюда, пропуская их через лес уродливого кода, для работы в таком ключе вообще не приспособленного))

за что точно надо отрывать руки - это когда противники хранимок гоняют гигабайты данных туда-сюда, пропуская их через лес уродливого кода, для работы в таком ключе вообще не приспособленного

"В детстве , я таких убивал. Из рогатки".(с)

Хотел написать всё тоже самое с примерами с предыдущих мест работы. Но нашёл ваш комментарий, с которым полностью согласен, и меня отпустило. Похоже, автор статьи просто не умеет готовить крокодилов. Хочу только напомнить ему, что каждый инструмент имеет свои границы применимости, и каждый специалист применяет их в меру своей квалификации.

Следующим шагом объявим рудиментом еще и функции. Ведь их отличия от процедур минимальны. Ну а напоследок можно и SQL объявить рудиментом, так как NoSQL СУБД имеют ряд преимуществ.

Или все же "кесарю кесарево"? Может "золотого молотка" не бывает и любой инструмент имеет свою область применения?

Уже. Функции - это тоже хранимки.

Ага, и если нужна кастомная агрегатная функция, то ни-ни - тащи все строки на клиента и там агрегируй?

Все описанные минусы следуют из принятой как стандарт архитектуры, подразумевающий разделение приложения на слои. Слой данных в ней рассматривает СУБД как просто место хранения табличных данных. Но даже при таком подходе обычные crud операции можно реализовать как ХП, и вызывать их, просто передавая параметры в ХП и, если нужно, получать результат. Это будет гарантировано быстрее, чём держать SQL код в коде другого языка и который будет компилироваться сервером бд каждый раз при его выполнении. Плюс выглядит это ужасно, каждый раз когда смотрю какой-нибудь @Repository с query native=true, испытываю неприятные ощущения. Какой уж тут clear code, когда на пол экрана висит строка с жутким SQL )

И аргумент, что SQL код начинает зависеть от конкретной реализации сервера, довольно слабый. Кто-то может привести пример, когда постоянно нужно менять сервер? На моей очень большой практике разработки такое было только два раза. Один раз, когда заказчик сразу сказал, что у него Oracle, и MS SQL ему не нужен, перепишите все. И второй когда вот это вот все началось и все ринулись импортозамещать все что можно, и решение нужно было перевести на postgres. Но оно уже было на orm и переход проходил довольно гладко

Но даже при таком подходе обычные crud операции можно реализовать как ХП, и вызывать их

Можно. Только нафига?

Это будет гарантировано быстрее

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

В среднем на 30% быстрее относительно запросов от orm на .NET + кластер MS SQL при нагрузке 120-150к запросов юзеров в минуту. Измерено на реальной банковской системе дбо. За сколько окупится не считали, за пару минут наверное. Для систем с уровнем надёжности mission critical не важно как быстро это окупится, имиджевые потери от падений системы кратко дороже

Откуда предположение, что логика в приложении обязательно означает падение системы? В Гугле тоже важна надежность, и падение тоже стоит дорого, но я сомневаюсь, что там логика на хранимых процедурах. И даже если считать, что для таких систем использование хранимых процедур оправдано, то их не так много, а для остальных утверждения в статье будут верными.

за пару минут наверное

Откуда такая оценка? Если час работы senior-программиста допустим 2000 р, то экономия за минуту работы сервера получается 1000 р. Если это 30%, то работа сервера стоит 3000 р в минуту или 130 млн рублей в месяц. Как-то многовато для сервера.

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

Падения системы обязательно будут в любом случае. Это жизнь. Но вероятность их возникновения при наличии транзакций, объединяющих множество запросов к БД, намного выше, когда логика в приложении, а не в СУБД. Просто потому, что сервис приложения может упасть, зависнуть, потерять связь с БД. И транзакция будет удерживать блокировки вплоть до истечения таймаута и закрытия соединения с приложением со стороны СУБД. А за это время может такая очередь выстроится, что запросы уже начнут отстреливаться по таймаутам или дидлокам.

Ошибкой в хранимой процедуре уложить СУБД - надо очень постараться. А ошибкой в коде сервиса уложить или зациклить этот сервис - легко и просто. У меня, судя по Grafana, таких проблем около десятка ежедневно. Все же система активно развивается и ошибок не избежать. Но рестарт контейнера одного инстанца одного сервиса - это мелочи, по сравнению с остановкой всех инстанцов сотен сервисов, работающих с одной СУБД.

Полностью согласен.

Наконец-то адекватный комментарий от человека с хайлоад нагрузкой и про финансовые данные.

Отсутствие хранимок при crud операциях это точно не про хайлоад Энтерпрайз.

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

Только нафига?

Например, с завидной регулярностью вижу от ORM такие запросы, которые по производительности уступают оптимизированному коду на plpgsql даже не на порядок, а на два-три. Можно, конечно, отложив в сторону ORM, предложить .NET разработчику включить в свой код целый ворох кода на plpgsql. Но тогда не только этот разработчик, но и любой поддерживающий этот код, должен очень хорошо знать не только C#, но и plpgsql. А можно этому разработчику предоставить представления, функции или процедуры, написанные другим разработчиком, специализирующимся на plpgsql.

Теоретически, когда то в будущем, может и появятся ORM умеющие использовать в CTE INSERT/UPDATE/DELETE RETURNING, применять pg_variables или переменные-массивы композитного типа вместо временных таблиц, понимать когда CTE надо явно материализовать, когда принудительно JOIN надо пребразовать в JOIN LATERAL или подзапрос и т.п. Но пока таких ORM даже не горизонте не видно.

Через сколько часов работы приложения окупится дополнительный час работы программиста?

Бывает, что в первые же минуты использования. Например, кто первый ответит на заявку, оперативно и достаточно точно спрогнозировав её доходность и сроки исполнения, тот, с большой вероятностью, и станет подрядчиком. А доходность заявки может составлять десятки тысяч рублей.

вижу от ORM такие запросы, которые по производительности уступают оптимизированному коду на plpgsql даже не на порядок, а на два-три

Это не отвечает на заданный вопрос. Вы неявно предполагаете, что увеличить сложность поддержки, чтобы ускорить запросы для CRUD-опреаций на 2-3 порядка, почему-то оправдано. А вопрос был о том, почему.

Например, кто первый ответит на заявку, оперативно и достаточно точно спрогнозировав её доходность и сроки исполнения

Непонятно, что это за заявки, и как ответ базы 100ms вместо 10 мешает пользователям вашей системы делать прогнозы.

Вы неявно предполагаете, что увеличить сложность поддержки, чтобы
ускорить запросы для CRUD-опреаций на 2-3 порядка, почему-то оправдано. А вопрос был о том, почему.

Потому что для пользователя разница между получением ответа через несколько секунд или несколько десятков минут определяет, захочет ли он вообще этой системой пользоваться.

Непонятно, что это за заявки

Да не важно. Любые заявки подразумевающие приоритезацию по очередности отклика на них.

как ответ базы 100ms вместо 10 мешает пользователям вашей системы делать прогнозы.

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

Просто представьте себе задачу, где полтерабайта оперативки и 256 ядер для PostgreSQL мало, даже не смотря на то, что запросов (вызовов хранимых процедур) он получает всего десятки в секунду, а целый ряд агрегаций отданы на откуп ClickHouse. Вы всерьёз предлагаете нарастить мощность такого кластера на порядок, чтобы сэкономить несколько часов работы разработчика?

Потому что для пользователя разница между получением ответа через несколько секунд или несколько десятков минут

Разговор был про обычные CRUD-операции, обычно это означает, что пользователь делает одну CRUD-операцию за одно взаимодействие с системой (условная кнопка "Сохранить"). Они не занимают десятки минут.

Если у вас какая-то сложная обработка данных, то не проще ли ее распараллелить в коде приложения на 1000 потоков?
Если обработка простая, но данных много, то возможно да, тащить их в приложение нецелесообразно, но таких проектов мало, и непонятно, зачем проекты другого рода делать так же. Вы усложняете поддержку, но получаете профит, а в других проектах поддержка усложнится, а такого профита не будет.

Во-вторых, в реальной жизни за 100 мс оптимизационную задачу не решите

Было утверждение "Обычные crud операции можно реализовать как ХП", я спросил "Нафига?". При чем тут оптимизационные задачи?

Вы всерьёз предлагаете нарастить мощность такого кластера на порядок, чтобы сэкономить несколько часов работы разработчика?

Нет, я задавал вопрос про обычные CRUD-операции.

Разговор был про обычные CRUD-операции, обычно это означает, что пользователь делает одну CRUD-операцию за одно взаимодействие с системой (условная кнопка "Сохранить").

И я об этом же. Пользователь запрашивает, будет ли положительная доходность от конкретной заявки, если один из ста тысяч своих вагонов, в зависимости от их расположения, загрузки, движения, технического состояния и стоимости переадресации он направит на станцию погрузки заявки, перевезет груз на станцию разгрузки и направит на станцию следующей заявки или отстоя. И это всё вполне ограничивается CRUD операциями. Никаких иных запросов к БД при этом не происходит.

А сохранения заказа на закупку (условная кнопка "Сохранить") с резервированием единиц комплектующих и материалов на складах и расчетом сроков исполнения этого заказа с учетом SCM и загрузки оборудования - тоже содержит только CRUD операции. И если это заказ на закупку, например, шагающего экскаватора, то сохраняться он вполне может минуты.

Они не занимают десятки минут.

Какие десятки минут? У меня полный перерасчет прогнозирования занимает свыше 30 часов. Один SQL запрос трансформации истории операций миллиона грузовых вагонов (сейчас около 1.4 миллиона в сети РЖД) за последние пять лет во временные серии выполняется час на 24 ядрах. Больше бессмысленно выделять, так как СХД не справляется.

Если у вас какая-то сложная обработка данных, то не проще ли ее распараллелить в коде приложения на 1000 потоков?

Подняв 1000 соединений с СУБД? Или сначала загрузить десяток терабайт из БД в приложение, а потом уже параллелить их обработку? Причем далеко не все запросы вообще-то параллелятся.

Было утверждение "Обычные crud операции можно реализовать как ХП", я спросил "Нафига?".

Вы искренне считаете, что хранимая процедура обязательно должна содержать не только десятки или сотни CRUD операций, но еще что-то другое? Или для Вас одна транзакция - это всегда один SQL запрос?

При чем тут оптимизационные задачи?

Потому что без них ни один вид бизнеса не обходится. Само название ERP, подразумевает решение оптимизационных задач в целях планирования ресурсов предприятия.

И я об этом же.

Нет, не о том же. Объем вычислений за одно действие пользователя в вашем примере не такой, какой происходит при нажатии кнопки "Сохранить" в обычной админке.
Почему-то не получается ни у кого обсуждать нормально без подмены понятий.

И это всё вполне ограничивается CRUD операциями.

А я сказал про одну операцию. Именно потому что в БД вообще всё сводится только к CRUD-операциям со строками в таблицах. Поэтому различие сводится только к количеству таких операций за одно действие пользователя.

И если это заказ на закупку, например, шагающего экскаватора, то сохраняться он вполне может минуты.

Ну и пусть сохраняется. Это никак не отменяет факта, что в большинстве админок, интернет-магазинов и других аналогичных приложений сохранение данных столько не занимает. Пример - отправление коммента на Хабре.

Какие десятки минут?

Я повторил ваши слова, не знаю какие десятки минут вы имели в виду.
"Потому что для пользователя разница между получением ответа через несколько секунд или несколько десятков минут"

Подняв 1000 соединений с СУБД? Или сначала загрузить десяток терабайт из БД в приложение, а потом уже параллелить их обработку?

Для разных задач может быть по-разному.

Вы искренне считаете, что хранимая процедура обязательно должна содержать не только десятки или сотни CRUD операций, но еще что-то другое? Или для Вас одна транзакция - это всегда один SQL запрос?

Ни то, ни другое. Не вижу никакой связи с моими словами.
Разговор был о том, что вместо простого INSERT надо делать процедуру, которая будет делать INSERT, и вызывать из приложения ее. Вместо простого DELETE надо делать процедуру, которая будет делать DELETE, и вызывать из приложения ее. И т.д.

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

Это ложь, я работал в компаниях, где не было десятков миллионов прогнозов и путей в графе, и большинство запросов к базе занимало меньше 100ms.

Объем вычислений за одно действие пользователя в вашем примере не такой, какой происходит при нажатии кнопки "Сохранить" в обычной админке.

Но даже меньше, чем при нажатии кнопки "Сохранить" в обычной ERP, что я специально показал. Сколько пользователей работают с ERP, а сколько с админками?

Пример - отправление коммента на Хабре.

То есть объединить в одном запросе SELECT и INSERT можно, а больше уже нельзя? )))

А любой интернет-магазин, сохраняющий в одной транзакции и заголовок заказа, и его детали - уже обходится без CRUD операций? )))

А я сказал про одну операцию. Именно потому что в БД вообще всё сводится только к CRUD-операциям со строками в таблицах.

Один SQL запрос, в общем случае, содержит одновременно INSERT, UPDATE, DELETE и SELECT, Вы действительно считаете, что это уже не CRUD операции?

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

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

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

Которые Вы предложили, согласившись на падение производительности на два-три порядка.

"Подняв 1000 соединений с СУБД? Или сначала загрузить десяток терабайт из БД в приложение, а потом уже параллелить их обработку?"

Для разных задач может быть по-разному.

А вот тут просьба подробней. Как по первому, так и по второму вопросу. С примерами.

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

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

Hidden text

Сколько пользователей работают с ERP, а сколько с админками?

Понятия не имею, у нас разговор про архитектуру кода проекта, а код один для всех пользователей сервиса.

- Объем вычислений за одно действие пользователя в вашем примере намного больше, чем в обычной crud операции в админке.
- Но даже меньше, чем при нажатии кнопки "Сохранить" в обычной ERP.

Я не понимаю смысл этих слов.

сохраняющий в одной транзакции и заголовок заказа, и его детали - уже обходится без CRUD операций?

Нет.

Один SQL запрос, в общем случае, содержит одновременно INSERT, UPDATE, DELETE и SELECT

Нет, в общем случае он не содержит эти операторы одновременно.

Потрудитесь назвать вид бизнеса, который обходится без оптимизационных задач.

Не буду, потому что это не относится к вопросу, зачем обычные crud операции реализовывать как ХП.

То есть объединить в одном запросе SELECT и INSERT можно, а больше уже нельзя?

По условиям утверждения "обычные crud операции можно реализовать как ХП" нельзя. Потому что тогда это будет не обычная crud операция.

разговор был о том, что объединить целый ворох SQL запросов одной транзакции в одну процедуру логичней

Я не знаю, где вы увидели это разговор. Я спрашивал про утверждение "обычные crud операции можно реализовать как ХП, и вызывать их, просто передавая параметры в ХП". Редактирование полей одного объекта в админке это обычная crud операция. Анализ десятков миллионов прогнозов и путей в графе это не обычная crud операция.

- Я повторил ваши слова про десятки минут
- Которые Вы предложили

Нет, фраза "несколько десятков минут" впервые появляется в вашем комментарии. Похоже, вы не следите за дискуссией и спорите о чем-то своем.

На нашем среднем сервере один запрос из приложения к базе занимает порядка 0.4-2 миллисекунды. Разница на 3 порядка это 2 секунды. А не десятки минут.
И это я посчитал время целиком с передачей по сети из базы в приложение, а не только сам запрос. А с процедурой время передачи по сети останется таким же, и разница будет еще меньше.

А вот тут просьба подробней. Как по первому, так и по второму вопросу. С примерами.

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

когда утверждение, что "ни один вид бизнеса не обходится без оптимизационных задач", оппонент пытается опровергнуть уже не видами бизнеса, а конкретными компаниями

Потому разговор был о том, как организованы конкретные проекты в конкретных компаниях, которые отправляют запросы к конкретным базам. Я спросил, почему их надо делать именно так, как посоветовал автор начального комментария. Если вы поменяли тему разговора, никому не сказав, то не надо удивляться, что вас никто не понимает. Мысли читать никто не умеет.
Я не вижу причин, почему мне надо делать хранимые процедуры в БД для моего сервиса с web-API, если где-то в этой же компании используется 1С с хранимыми процедурами в своей БД.

На нашем среднем сервере один запрос из приложения к базе занимает порядка 0.4-2 миллисекунды. Разница на 3 порядка это 2 секунды.

Хм, а ведь это даже неправильно. Это уже запрос из приложения в базу, который якобы должен быть больше на 3 порядка. То есть с процедурой должно быть 2 микросекунды, что явно неправдоподобно. В базе этот запрос выполняется за 0.3 - 1.8 миллисекунды.

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

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

По моему опыту разработки и внедрения ERP, автоматизации производства, логистики, АСКУЭ, биллинга коммунальных услуг и т.п - с точностью наоборот. Даже такие относительно небольшие предприятия, как Истра-Нутриция, Белая Дача или КамчатскЭнерго без этого не обходятся. Я уж молчу о предприятиях из ТОП-200 в РФ или международных холдингах. А более мелкие предприятия редко могут позволить себе заказные разработки и вынуждены обходиться коробочными решениями.

Я говорил на основании своего опыта и анализа требований в вакансиях при поиске работы.

https://www.searchlogistics.com/learn/statistics/ecommerce-statistics/
"There are currently over 26 million ecommerce sites and stores worldwide"

Можно взять эту статистику. В большинстве интернет-магазинов нет столько данных, чтобы их надо было обрабатывать процедурами на серверах с сотнями ядер. Я сомневаюсь, что предприятий уровня КамчатскЭнерго в мире больше 26 миллионов. А во многих интернет-магазинах есть еще и внутренние проекты, где тоже хранимые процедуры не используются. Еще можно поискать количество вакансий со знанием pl/sql на hh.ru.

Я говорил на основании своего опыта и анализа требований в вакансиях при поиске работы.

"There are currently over 26 million ecommerce sites and stores worldwide"

Вы сами поняли, что написали? Ecommerce - это как раз классический пример использования тех самых заказов на закупку, о которых я писал выше. Как Вы будете формировать заказ без резервирования товара на складах и прогнозирования времени его исполнения? Это даже не трогая механизмы оплаты, которые никак "простой обработкой небольших массивов данных" не реализуются.

Я сомневаюсь, что предприятий уровня КамчатскЭнерго в мире больше 26 миллионов.

Если мы оцениваем востребованность разработки, то сравнивать надо количество сотрудников. Например, в РФ в малом и среднем бизнесе занято ~28 млн. человек при общем числе занятых ~73 млн. человек.

И это даже не считая, что я указал: "более мелкие предприятия редко могут позволить себе заказные разработки и вынуждены обходиться коробочными решениями"

При этом пользователей только 1С в РФ свыше 8 миллионов. То есть, каждый десятый занятый использует "сложные обработки массивов данных" пусть даже и не всегда "больших". А если добавить сюда остальные ERP, то легко выйдем к каждому второму пользователю компьютером на рабочем месте.

Как Вы будете формировать заказ без резервирования товара на складах и прогнозирования времени его исполнения?

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

Если мы оцениваем востребованность разработки, то сравнивать надо количество сотрудников.

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

То есть, каждый десятый занятый использует "сложные обработки массивов данных"

Ну и пусть использует. Как из этого следует, что мне надо писать свои обычные crud операции в хранимых процедурах, если любая их них занимает меньше одной секунды даже с логикой в приложении?

Да ответ-то на всё простой - дизайнить систему надо правильно: бизнесу - бизнесово, базе - базово. Тогда всё уляжется. Отцы-основатели ООД и ООА свое дело туго знали, не то что нынешнее племя...

Я бы сказал так.

Для юзер-ориентированных приложений, построненный по трех-уровневой архитектуре - хранимки "это рудимент". В новых приложениях использовать такие решения крайне плохо и никакая "лапша на уши" про 2% выигрыша производительности того не стоит.

Одно размазывание логики чего стоит, плюс полная невозможность переиспользования реализации в разных слоях.

------------

Но есть куча задач в самой БД либо для переливки\подготовки данных. Тут хранимки - это хорошо и правильно.

----------

Немного по пунктам

Сложность в тестировании

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

Автоматическое тестирование или unit-тесты

Тут я не понял, в чем сложность. Возможно просто автор чуток не умеет в определенные вещи

Например, от бизнес-партнеров поступило требование собирать метрики по определённым участкам бизнес-процесса для их оценки. И как мы будем это делать? Правильно, реализовывать ещё больше костылей, потому что SQL — это язык для агрегации данных, а не язык для описания бизнес-логики приложения. В любом современном языке программирования достаточно подключить какую-либо библиотеку и настроить URL /metrics для сбора необходимых метрик

И да, и нет. Зависит от метрик. Ну и надо понимать, что сбор метрик в интеграционной среде часть это много разных приложений, что уже не так сильно зависит от конкретных технологий реализации

Любая категоричность а подобных вопросах - вещь сильно спорная. Вот IBM в своё время чуть не разорилась из-за злоупотребления оператором goto, и потом целое поколение программистов шарахалось от этого оператора, как чёрт от ладана. Многие даже и не знали первопричины. Просто - детская травма :-)

Второй пример из области БД: триггеры - это зло! Теперь - хранимые процедуры, как оказалось, тоже зло.

Возможно, просто надо разобраться, что для чего использовать, и в каких количествах?

"чуть не разорилась из-за злоупотребления оператором goto". Что вот прям вот это причина не использовать GOTO в коде?! 🤔 У меня как-то от тех времён другое представление осталось 😳...

Ну, чуть упростил историю про спагетти-код и ОС, которую никак не могли дописать, в результате чего появилась теория и практика структурного программирования... :-) Но запрет на goto был следствием произошедшего, нет?

Возможно... Просто я с содроганием вспоминаю свой опыт перевода небольшой части пакета Графор с Фортрана на Паскаль (ну, нужно нам было на Паскале строить какие-то графики, а самым простым способом в тех условиях было взять готовую профессиональную библиотеку и как-то частично выдрать из неё нужное). То есть, мне казалось всегда, что это всё-таки, в первую очередь, вызвано именно спецификой самого оператора, а не только лишь каким-то конкретным историческим событием.

Про "запрет" как-то странно читать, конечно, когда оператор goto спокойно себе существует даже в современных (сравнительно) структурных языках (да в с#, хотя бы). Но вот для чего он там существует - мне совершенно непонятно. Это как рекурсия - приём существует, но пользоваться им чревато...

Особенно круто проявляет себя аналог goto в машинном коде, когда всё написал, посчитал смещения для переходов и вдруг потом выясняется, что надо вставить пару-тройку команд в середине. Или в ранних версиях Бейсиков, когда при правке кода выясняется, что зазоров в нумерации строк не хватает для внесения необходимых исправлений 😁.

А в какой версии Бейсика команда RENUM появилась?

Если она даже в той версии была, я, скорее всего, про нее не знал... Но это шла речь о Basic -3A под Д3-28 🙂...

А плюсах я им не пользовался, а вот в С использовал регулярно, чтобы перебрасывать управление в конец функции и обрабатывать там ошибки. Вместо исключения.

Опять же, всё зависит от того, как пользоваться. Немаленькую систему написанную на Фортран II без особых проблем перевели на АСВТ Паскаль примерно в те же годы. (ПРИС)

Могу рассказать зачем goto в сишарпе) Компиляция идет в 2 этапа. Сперва код преобразуется в низкоуровневый си шарп, в котором отсутствует большинство конструкций. К примеру из циклов там есть только while, в который преобразуются все остальные. На этом этапе происходит масса оптимизаций. И только потом низкоуровневый сишарп компилируется в msil. Так вот компилятор иногда использует метки и goto в низкоуровневом коде. Это бывает при разворачивании свичей, для выхода из циклов, или вложенных конструкций, при обработке исключений. Он компилятор, ему можно. Ну и собственно поэтому goto не удаляют из спецификации языка. Хотя использовать его не-компиляторам конечно же не стоит.

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

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

Про конечный автомат не слышал, но ретрай паттерн на гоуту выглядит лаконичнее и чище, чем на цикле. Только никому ни слова ) Я все равно так никогда и не решился его в живом проекте написать.

Увы, мода рулит всем. Но злоупотребление goto - это действительно такое себе.

Возможно, просто надо разобраться, что для чего использовать, и в каких количествах?

Так автор вроде бы так и делает? Говорит, что для бизнес-логики надо использовать код в приложении, а хранимые процедуры надо использовать в количестве 0. Приводит свои аргументы для этого утверждения.

Задача, которую я только что решал: односторонняя (слава богу) синхронизация данных Oracle -> Postgres. Можно, конечно, обойтись без stored procedures...

Вторая задача. Данные поступают из другой системы и сразу должны быть трансформированы/обогащены. Тоже можно решить без встроенных процедур и триггеров. А надо? И это - таки-часть бизнес логики.

Я не очень понимаю, как это связано с утверждением, на которое я отвечал. Вы высказали претензию, что автор что-то делает не так, и должен делать по-другому. Я сказал, что с моей точки зрения он и так уже делает "по-другому", и поэтому непонятно, что вы от него хотите.

Это связано с тем, что требования к качеству работы приложений возросли, а новых инструментов в SQL для решения этой проблемы так и не появилось, что не позволило повысить качество.

Невозможно относиться к подобным сентенциям иначе, чем к древнему "Кому сейчас нужна командная строка, если всё можно сделать мышкой". Инструмента, видите ли, нету...

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

Переводя на русский - программист с хорошим знанием SQL уволился, а вместо него наняли другого, который в SQL ни ухом, ни рылом.

А виновата, как обычно, хранимая процедура.

Размазанность бизнес логики

Вот интересно, вас использование в бизнес-логике различных функций и процедур из "стандартных" библиотек не раскорячивает? Или если процедура поставляется с ОС/фреймворком/средой/прочее - то это built-in, а если сам написАл - то это бизнес-логика?

Любая бизнес-логика составлена из кучи элементарных операций. Сортировка, поиск, фильтрация, агрегация, расчёт, сравнение, выбор, и т.д., и т.п. И где-то есть та граница, после которой несколько последовательных элементарных операций становятся бизнес-логикой, специфической для конкретного процесса, хотя вот только что эта последовательность ещё была последовательностью достаточно стандартных операций, просто со специфическими константами, ограничениями и пр. И если эта последовательность переносится в хранимую процедуру, то бизнес-логика совершенно не страдает и не дробится. Просто отлаживать надо лучше, условий учитывать больше, входные проверять жёстче, документировать качественнее. Должно быть - "написал и забыл".

А SQL-сервер обрабатывает данные всяко лучше, чем программист напишет.

Переводя на русский - программист с хорошим знанием SQL уволился, а вместо него наняли другого, который в SQL ни ухом, ни рылом.

Нет, это означает, что какая-то процедура пишет в какую-то таблицу число 42, которое неизвестно что обозначает, и неизвестно кто его читает.

Вот интересно, вас использование в бизнес-логике различных функций и процедур из "стандартных" библиотек не раскорячивает?

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

а если сам написал - то это бизнес-логика?

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

Процедура реализует правила и ограничения бизнеса - это бизнес-логика. Не реализует - не бизнес-логика. Условно говоря, бизнес-логика это код, который вы меняете при изменении бизнес-требований.

то бизнес-логика совершенно не страдает и не дробится

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

какая-то процедура пишет в какую-то таблицу число 42, которое неизвестно что обозначает, и неизвестно кто его читает.

Ну то есть вся проблема - в хреновом документировании, что ли?

Процедура реализует правила и ограничения бизнеса - это бизнес-логика.

Вот только не надо тёплое да мягкое в одну кучу, да... Процедура реализует правила и ограничения, которые передаются в неё значениями параметров. Всё. Точка.

А вызов этой процедуры передаваемыми параметрами задаёт используемые ограничения и параметры применения правил в соответствии с требованиями бизнеса - вот сам вызов и есть бизнес-логика. Но он не на SQL сервере. а в вашем бизнес-приложении.

Вот если вы и параметры, необходимые бизнес-процессу, захардкодили в процедуре, то да, она как-то уже стала не общей процедурой, а куском вашей бизнес-логики. Но кто в том виноват-то?

Ну то есть вся проблема - в хреновом документировании, что ли?

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

Процедура реализует правила и ограничения, которые передаются в неё значениями параметров.

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

А вызов этой процедуры передаваемыми параметрами и есть бизнес-логика

Нет. Есть процедура "Создание заказа". Она вызывается только с id корзины, где есть список товаров. Есть бизнес-требование "Если есть товары из группы А, добавить дополнительную строку доставки 1000 рублей, потому что требуется грузчик". Его реализация это бизнес-логика. Вы это регулируете параметрами процедуры или if в коде процедуры?

Дальше появляется бизнес-требование "Если есть товары из группы Б, добавить дополнительную строку доставки 2000 рублей, потому что требуется 2 грузчика. Если при этом есть товары из группы А, то добавлять 1000 рублей не надо". Для его реализации вы добавите параметр в вызов процедуры, будете вместо id корзины передавать что-то другое, или поменяете код процедуры?

Есть процедура "Создание заказа". Она вызывается только с id корзины, где есть список товаров. Есть бизнес-требование "Если есть товары из группы А, добавить дополнительную строку доставки 1000 рублей, потому что требуется грузчик". Его реализация это бизнес-логика. Вы это регулируете параметрами процедуры или if в коде процедуры?

Я добавлю эту дополнительную строку в список товаров корзины ПЕРЕД вызовом процедуры.

Процедура должна выполнять именно операцию создания заказа по переданному ей списку товаров с количествами и вернуть результаты своей работы (номер созданного заказа либо признак/код ошибки создания заказа). Это элементарная операция за пределами бизнес-логики. Добавление же строки - это бизнес-логика, которая реализуется в коде приложения, либо до вызова процедуры добавлением нужной строки в состав корзины (в процессе создания корзины в момент добавления в неё товара группы А, либо в момент финализации создания корзины и перехода к созданию заказа), либо после неё получением характеристик созданного заказа и вызовом процедуры корректировки заказа.

С кодом в приложении такого документирования требуется заметно меньше,

Вот этого не понимаю от слова "совсем". Есть "число 42". В документировании должна быть добавлена строка, которая разъясняет, что это за значение, куда и в каком случае пишется и т.п. Объясните мне, почему эта строка в документировании приложения будет короче. чем в документировании процедуры...

Или вы хотите сказать, что в процедуре это задокументировать нужно, а в приложении нет? Ну тогда мы снова возвращаемся к тому, что новый сотрудник дока в используемом ЯП (и так знает, что это за "42") и ноль в SQL.

Есть "число 42". Объясните мне, почему эта строка в документировании приложения будет короче. чем в документировании процедуры.

В приложении нет "числа 42", есть конкретная константа с говорящим названием в каком-то классе EmailTemplate::ORDER_CREATED = 42;, которая используется в разных функциях в коде вместо числа 42. Ее не нужно дополнительно документировать где-то еще. А в процедурном SQL такие константы сделать нельзя, максимум в каждой процедуре где нужен этот шаблон будет своя переменная SET tempateOrderCreated = 42;. И если появится новый шаблон письма для создания заказа с другим id, то надо будет искать и менять во всех местах.

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

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

У меня именно это и написано. "Создание заказа по переданному ей списку товаров" означает, что в корзине есть только список товаров, а процедура создания заказа должна сама рассчитать все необходимые платежи.

Это элементарная операция за пределами бизнес-логики.

Если у вас процедура это простая обертка для элементарной операции INSERT, то непонятно, зачем она нужна.

Я добавлю эту дополнительную строку в список товаров корзины ПЕРЕД вызовом процедуры
Добавление же строки - это бизнес-логика, которая реализуется в коде приложения

Вы же говорили про перенос бизнес-логики в хранимую процедуру:
"Любая бизнес-логика составлена из кучи элементарных операций. И если эта последовательность переносится в хранимую процедуру, то бизнес-логика совершенно не страдает и не дробится".

Если у вас часть реализации бизнес-требований находится в приложении, а часть в хранимой процедуре, то это и есть размазанность бизнес-логики. А потом программисту приложения надо разбираться, где какая часть бизнес-требований реализована, и как это всё вместе работает.

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

Да-да, вот эти вот скрытые знания "Нельзя просто вызвать процедуру "Создать заказ(id корзины), перед вызовом надо добавить в эту корзину доставку", это и есть "собирать информацию по крупицам".

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

Вы же говорили про перенос бизнес-логики в хранимую процедуру:"Любая бизнес-логика составлена из кучи элементарных операций. И если эта последовательность переносится в хранимую процедуру, то бизнес-логика совершенно не страдает и не дробится".

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

У бизнеса вообще нет такой операции "Добавить доставку в корзину", в корзину можно добавлять только товары и коды скидок.

Цитирую ваши слова: "Есть бизнес-требование "Если есть товары из группы А, добавить дополнительную строку доставки 1000 рублей, потому что требуется грузчик". Его реализация это бизнес-логика." То есть требование есть, логика есть а операции нет? Хотя строка "Доставка" финально-таки есть. Непонятно.

Но вот таких передёргиваний не ожидал.

Объяснением вы конечно себя утруждать не хотите? "Вы меня поняли неправильно, а как правильно не скажу"?
Вы написали комментарий, я с самого начала говорю в его контексте. В нем вы не говорили про бизнес-логику в приложении, а приводили аргументы в защиту логики в процедурах.

Цитирую ваши слова:
добавить дополнительную строку доставки

Так не сказано же, что надо добавить ее в таблицу корзины. Я написал "Есть процедура "Создание заказа". Она вызывается только с id корзины, где есть список товаров", значит строку доставки надо добавить в заказ при создании. Чтобы она потом отображалась пользователю, и ее можно было учитывать при возвратах. "Строка доставки" это бизнес-термин из бизнес-требований.

То есть требование есть, логика есть а операции нет?

Операции "Добавить строку доставки в корзину" нет. Есть операция "Создание заказа", одно из ее действий это добавить строку доставки в создаваемый заказ по заданным условиям.

Объяснением вы конечно себя утруждать не хотите? "Вы меня поняли неправильно, а как правильно не скажу"?

Давайте обратимся к полной версии текста.

Любая бизнес-логика составлена из кучи элементарных операций. Сортировка, поиск, фильтрация, агрегация, расчёт, сравнение, выбор, и т.д., и т.п. И где-то есть та граница, после которой несколько последовательных элементарных операций становятся бизнес-логикой, специфической для конкретного процесса, хотя вот только что эта последовательность ещё была последовательностью достаточно стандартных операций, просто со специфическими константами, ограничениями и пр. И если эта последовательность переносится в хранимую процедуру, то бизнес-логика совершенно не страдает и не дробится.

Теперь посмотрим, что вы оставили при цитировании.

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

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

А что оставили вы? Вы оставили часть текста, которая утверждает, что агрегировать можно без оглядки на такую границу.

Я говорил про контекст обсуждения, а не про конкретное высказывание, поэтому процитировал то, что задает контекст. Вы с самого начала говорили про размещение бизнес-логики в коде процедур, сказали "SQL-сервер обрабатывает данные всяко лучше, чем программист напишет", поэтому я привел пример процедуры на SQL-сервере с бизнес-логикой создания заказа. А потом вы ни с того ни с сего сказали, что бизнес-логику в этом примере надо размещать в приложении. Поэтому я указал на кажущееся мне несоответствие. Если вы согласны, что в таких случаях правильнее размещать бизнес-логику в приложении, то непонятно, с чем вы спорите тогда.

К примеру на этих ваших ms sql'лях можно .net хранимки делать, на постгресах всякие там pl/python, и тд. и тп., так что всё зависит от прокладки между...

DBA-разработчики?

Разработчики администраторы баз данных?

Капризный плач малолетнего кодера-неумехи.

Можно, конечно, пропуская картинки и абзацы с лирикой, разобрать статью по пунктам, начиная с "когда отцы-основатели данной хранимой процедуры покидают компанию, вместе с ними уходит и понимание того, как всё это должно функционировать" (если такие "отцы" напишут бизнес-логику на стороне приложения и тоже исчезнут, не оставив документации, то эффект будет ровно тот же), но смысла в этом немного, как и в том, что можно написать симметричную статью про плюсы размещения бизнес-логики на стороне БД. Задачи бывают разные, решения под них нужны тоже разные и в этом смысле в статье не хватает главного - рассказа автора о кейсах, в которых ему приходилось размещать бизнес-логику в разных компонентах ИС. А так получился неоригинальный (на мой взгляд, просто набор банальностей) и однобокий взгляд на вопрос.

Мне почему-то кажется, что в названии статьи не хватает тире перед словом рудимент. Собственно, именно поэтому и открыл, поскольку сразу не смог понять о чем речь: то ли о процедуре рудимента(кстати, а есть такая?), то ли о процедуре, как о рудименте. Впрочем, это возможное следствие затухающего сознания перед сном; в следующий раз попробую статьи с интригующим названием откладывать на утро.

Все зависит от задач и архитектуры ПО. Мало кто понимает как правильно создавать архитектуру подобных приложений с логикой на сервере. В приложениях локальной сети я использую только хранимые процедуры и функции SQLServer, на клиенте Delphi c FIREDAC и LABVIEW c ODBC. Разработка не crud приложений, а сложных систем для производства и тестирования интеллектуальных датчиков и расходомеров. Кстати, есть и приложение для ремонта интеллектуальных приборов с элементами документооборота. Все приложения включены в систему мониторинга и логирования ( без всяких Кафка, все на SQL). Логика на сервере очень удобна для меня. Но нужно правильно делать архитектуру ПО, оставляя на клиенте только интерфейсные зависимости. Все изменения DDL посредством триггера в БД логируются в таблицу БД и всегда можно посмотреть изменения не только в хранимых процедурах, а вообще все изменения в БД. Одну из систем я описал в своей статье ( можно открыть публикацию из моего профиля на хабр.

https://habr.com/ru/users/ALexKud/publications/articles/

С десктопными приложениями есть свои особенности. С логикой на клиенте надо при изменениях обновлять все клиенты, а также на языках типа Delphi работа с хешмапами сложнее, чем SQL с динамической типизацией.

Поэтому если десктопное приложение коннектится напрямую к базе, то хранимые процедуры оказываются удобнее. Но дело в том что в данном случае это просто удобство логики на сервере, и можно сделать сервер на другом языке программирования, который более удобен для выражения бизнес-требований, чем процедурный SQL. Это также и более безопасно, чем давать пользователю прямой доступ к базе.

У нас клиенты и rest сервер на Делфи, с кешированием и прочим. При этом, использование хранимых процедур сильно повышают скорость выполнения сложных, комплексных запросов. Потому что не нужно гонять данные между сервером и бд.

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

Почему не нужно, если это упростит поддержку, уменьшит количество багов и время выполнения задач программистами? Может быть у вас оно не упрощает, но другие-то почему должны от этого отказываться, если у них упрощает.

Я абсолютно не против того, чтобы не пихать бизнес-логику в хранимки

Но те, кто об этом пишет статьи, обычно предлагают такие альтернативы:

  • аналогичный код на Питоне / JS / PHP..., ещё и обезображенный использованием ORM

  • dbt и его аналоги, которые в точности повторяют функционал хранимок, но через богомерзкую Jinja

Ещё ни разу не видел кейса, чтобы написали: "мы выпилили хранимки, вместо них написали высокопроизводительный код на С / Java / Rust..., который работает с данными по бинарному протоколу"

А замена хранимок на dbt / функции во внешних скриптах - это тот же половой орган, только в другой руке

--
По поводу именно этой статьи: баянистый сборник мифов о хранимках от человека, который вряд ли когда-то хотя бы пробовал писать на них что-то сложное. Иначе никак не объяснить множественные посылы о проблемах с тестированием - это ж самое простое как раз

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории