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

Изображение 8999

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

Harsha M V 10 июль 2012, в 08:18
Поделиться

Насколько велики базовые данные, используемые правилами? Насколько это сложно? Существуют различные подходы для 1000 строк в одной таблице с 10 столбцами против миллиардов транзакций, хранящихся в сложной структуре данных.

Gordon Linoff 18 июль 2012, в 14:31
Harsha M V 18 июль 2012, в 14:46

Насколько сложны бизнес-правила, которые вы хотите включить? Являются ли они в основном простыми условиями фильтрации для столбцов вместе с некоторыми новыми рассчитанными значениями? Или они более сложные?

Gordon Linoff 18 июль 2012, в 14:53
Harsha M V 18 июль 2012, в 15:06

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

Организация бизнес логики со сложной схемой данных. Тимофей Кукушкин.

Gordon Linoff 18 июль 2012, в 15:12

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

Harsha M V 18 июль 2012, в 17:37
Показать ещё 4 комментария
Поделиться:
business-rules
business-logic

11 ответов

Лучший ответ

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

Против бизнес-логики, хранящейся в базе данных

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

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

  • Бизнес-логика в базе данных
  • Бизнес-логика в хранимых процедурах
  • Сохранение условных логических выражений/правил в базе данных

Somnath Muluk 16 июль 2012, в 08:19
Поделиться

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

brianpeiris 18 июль 2012, в 14:24

Аргумент против бизнес-логики «мягкого кодирования» следующим образом: http://thedailywtf.com/Articles/Soft_Coding.aspx

«Причина, по которой мы находим» Мягкое кодирование «, состоит в том, что мы боимся перемен. Не нормальный Страх перемен, но страх, что код, который мы пишем, должен измениться в результате изменения бизнес-правила. страх иметь. Весь смысл программного обеспечения (следовательно,» мягкий «) заключается в том, что он может измениться, что он изменится. Единственный способ изолировать ваше программное обеспечение от изменений бизнес-правил — это создать полностью общую программу, лишенную всех бизнес-функций правила еще могут реализовать любое правило.О, и они уже создали этот инструмент, его называли С++, а Java, а С# и Basic. И, смею сказать, COBOL.»

Matt Kline 18 июль 2012, в 15:10
Поделиться

Изображение 110607

CREATE TABLE businessRule ( id INT NOT NULL , name VARCHAR(32) NOT NULL , description VARCHAR(255) NULL , statement VARCHAR(255) NOT NULL , PRIMARY KEY (id) ) ENGINE = InnoDB; CREATE TABLE leftOperand ( id INT NOT NULL , value VARCHAR(255) NOT NULL , PRIMARY KEY (id) ) ENGINE = InnoDB; CREATE TABLE ruleItem ( id INT NOT NULL , businessRuleId INT NOT NULL , operator ENUM(‘if’,’and’,’or’,’not’) NOT NULL , loperand INT NOT NULL , comparator ENUM(») NOT NULL , roperand VARCHAR(255) NOT NULL , roperand_ispercentage TINYINT(1) NOT NULL , PRIMARY KEY (id) , INDEX businessRule_FK (businessRuleId ASC) , INDEX leftOperand_FK (loperand ASC) , CONSTRAINT businessRule_FK FOREIGN KEY (businessRuleId ) REFERENCES mydb.businessRule (id ) ON DELETE CASCADE ON UPDATE RESTRICT, CONSTRAINT leftOperand_FK FOREIGN KEY (loperand ) REFERENCES mydb.leftOperand (id ) ON DELETE RESTRICT ON UPDATE RESTRICT) ENGINE = InnoDB;
RandomSeed 15 июль 2012, в 09:45
Поделиться
Пожалуйста, поделитесь примером того, как вставить данные в эти таблицы
saching 15 май 2018, в 17:43

Читайте также:  Какой бизнес можно открыть на 10000 рублей

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

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

Изображение 110608

Затем пришло время преобразовать его в ERD:

Изображение 110609

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

[ОБНОВЛЕНИЕ]

Например, если вы хотите сохранить оператор a + b * -c в базе данных, его можно перевести как следующие вставки:

— c INSERT INTO statement (statement_id) VALUES (1); INSERT INTO operand (statement_id, type) VALUES (1, ‘double’); — — (minus) INSERT INTO statement (statement_id) VALUES (2); INSERT INTO operator (statement_id, type) VALUES (2, ‘minus’); — -c INSERT INTO binary (operator_statement_id, operand_statement_id) VALUES (2, 1); — b INSERT INTO statement (statement_id) VALUES (3); INSERT INTO operand (statement_id, type) VALUES (3, ‘double’); — * (multiply) INSERT INTO statement (statement_id) VALUES (4); INSERT INTO operator (statement_id, type) VALUES (4, ‘multiply’); — b * -c INSERT INTO unary (operator_statement_id, operand_statement_id1, operand_statement_id2) VALUES (4, 3, 2); — a INSERT INTO statement (statement_id) VALUES (5); INSERT INTO operand (statement_id, type) VALUES (5, ‘double’); — + (plus) INSERT INTO statement (statement_id) VALUES (6); INSERT INTO operator (statement_id, type) VALUES (6, ‘sum’); — a + b * -c INSERT INTO unary (operator_statement_id, operand_statement_id1, operand_statement_id2) VALUES (6, 5, 4);
Mehran 18 июль 2012, в 09:51

Поделиться

Мехран, очень интересный подход. Не могли бы вы привести пример логики и вставку данных для этой структуры БД? Спасибо.

Paktas 20 сен. 2013, в 19:28
Я обновил ответ.
Mehran 21 сен. 2013, в 12:00

Не могли бы вы помочь мне, что бы выбрать запрос, если я хочу получить + B * -C
saching 14 май 2018, в 11:36
Mehran 16 май 2018, в 12:56
saching 20 май 2018, в 12:40
Это может быть так же просто, как SELECT * FROM

WHERE statement_id = ? как я сказал в моем предыдущем комментарии.

Mehran 20 май 2018, в 13:51
Показать ещё 4 комментария

Я думаю, что сначала нужно решить, нужно ли вам сначала вводить правила в базу данных.

Базы данных представляют собой тяжелое решение и часто просто не нужны.

Имея дело с механизмами правил в различных формах, включая базу данных, я могу сказать, что она может стать действительно разочаровывающей и непродуктивной, очень быстро. Одна из больших ошибок, которые я видел, — это попытка написать собственный язык правил ad-hoc и использовать это для управления условной логикой через базу данных. По крайней мере, используйте язык, который уже был проверен (Python, javscript и т.д.), И вставляйте туда.

Даже если правила достаточно сложны, я лично предпочитаю использовать электронные таблицы Excel. Мы используем это для автоматизации (для обработки переменной логики на основе даты вступления в силу и т.д.), И мы также составляем довольно сложную логику рейтинга страхования для скриптов Perl, взаимодействующих через веб-службу, используя этот продукт: http://decisionresearch.com/products/rating.html.

Контраст сохраняет логику в базе данных по сравнению, скажем, с электронной таблицей Excel:

  • Логика в базе данных сложнее тестировать и разрабатывать для сравнения с Excel, потому что Excel обеспечивает мгновенную обратную связь.
  • База данных менее (значительно меньше) выразительна по сравнению с Excel.
  • Вы можете покрасить код и добавить в Excel всевозможные визуальные подсказки, чтобы сделать условия ошибки и т.д., действительно выделяться.

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

То, что я получаю, — это то, что вы делаете правильный компромисс с точки зрения удобства использования/выразительности/тестируемости/производительности. Где я работаю, быть прав и быть продуктивным, важнее, чем быть быстрым в исполнении, поэтому мы идем с помощью службы Excel/web.

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

Читайте также:  Какие документы нужны для купли бизнеса

В моей компании это зависит от масштаба правил и того, как часто они меняются относительно того, с чем мы живем. Рейтинговое страхование — Excel, без вопросов. Некоторая государственная логика? Совпадающие файлы правил Java достаточно просто.

Источник: overcoder.net

Реализация бизнес-логики в MySQL

Привет Хабр! Хочу рассказать в статье мой опыт реализации бизнес логики (БЛ) в MySQL.

Есть разные мнения насчёт вопроса стоит ли хранить БЛ в базе. Я много лет работаю с Oracle и философия Oracle подразумевает, что БЛ в БД это Best Practices. Приведу пару цитат Тома Кайта:

Tom Kyte. Effective Oracle by Design
If the database does something, odds are that it does it better, faster and cheaper, that you could do it yourself

Том Кайт. Oracle для профессионалов.
Прежде чем начать, хотелось бы объяснить вам мой подход к разработке. Я предпочитаю решать большинство проблем на уровне СУБД. Если что-то можно сделать в СУБД, я так и сделаю. [. ] Мой подход состоит в том, чтобы делать в СУБД все, что возможно. [. ]
При разработке приложений баз данных я использую очень простую мантру:

  • если можно, сделай это с помощью одного оператора SQL;
  • если это нельзя сделать с помощью одного оператора SQL, сделай это в PL/SQL;
  • если это нельзя сделать в PL/SQL, попытайся использовать хранимую процедуру на языке Java;
  • если это нельзя сделать в Java, сделай это в виде внешней процедуры на языке C;
  • если это нельзя реализовать в виде внешней процедуры на языке C, надо серьезно подумать, зачем это вообще делать.

В то же время в среде web-разработчиков приходится слышать мнения, что БЛ в БД это чуть ли не антипаттерн. Но я не буду останавливаться на вопросе стоит ли реализовывать БЛ в БД. Пусть каждый решает сам. Тем, кто хочет посмотреть, что у меня получилось в свете не столь обширного (по сравнению с Oracle) инструментария MySQL, добро пожаловать под кат.

Реализация предполагает нативный вызов SQL-команд (INSERT/UPDATE/DELETE) на клиенте с описанием логики в триггерах. Всё дальнейшее описание будет справедливо для MySQL 5.1.73. Вот основные моменты, с которыми я столкнулся при разработке:

  • Безопасность на уровне строк (Row Level Security), см. мою предыдущую статью
  • Генерация ошибок в триггерах: увы, нативным методом в MySQL 5.1 ошибку не сгенеришь.
  • Удобное написание логики в триггерах: В MySQL нельзя создавать 1 триггер на разные SQL-команды, в итоге логика будет размазана по 6 подпрограммам
  • Запрет динамического SQL в триггерах
  • Отсутствие AFTER STATEMENT TRIGGER: в триггерах уровня строки запрещено менять таблицу в которую вносятся изменения, в Oracle эта проблема решается AFTER триггером уровня выражения

Генерация ошибок в триггерах

При обработке SQL-команды требуется прервать её выполнение с ошибкой. Например, если сумма документа превышает лимит, то прервать операцию INSERT/UPDATE и сообщить об ошибке:

CREATE TRIGGER docs_bef_ins_trg BEFORE INSERT ON docs FOR EACH ROW BEGIN DECLARE max_limit decimal(10,2); SELECT o.max_limit INTO max_limit FROM org o WHERE o.id = NEW.org_id_client; IF NEW.sum > max_limit THEN — . — Тут мы хотим прервать выполнение триггера — и выйти с ошибкой, но в MySQL нет нативных — способов сделать это — . END IF; END $

Поискав в интернете и слегка подправив решение, появился такой код:

И чтобы в php пользовательские SQL ошибки были с кодом -20000 и человеческим текстом ошибки:

Итоговый код триггера будет выглядеть так:

CREATE TRIGGER docs_bef_ins_trg BEFORE INSERT ON docs FOR EACH ROW BEGIN DECLARE max_limit decimal(10,2); DECLARE name VARCHAR(255); SELECT o.max_limit, o.name INTO max_limit, client_name FROM org o WHERE o.id = NEW.org_id_client; IF NEW.sum > max_limit THEN CALL raise_error(CONCAT(‘Сумма (‘, NEW.sum , ‘) по клиенту ‘, client_name , ‘ не может превышать лимит ‘, max_limit , ‘ в документе с NEW.id)); END IF; END

Или более красивый вариант с использованием функции:

CREATE TRIGGER docs_bef_ins_trg BEFORE INSERT ON docs FOR EACH ROW BEGIN DECLARE msg TEXT; SET msg := (SELECT raise_error(CONCAT(‘Сумма (‘, NEW.sum , ‘) по клиенту ‘, o.name , ‘ не может превышать лимит ‘, max_limit , ‘ в документе с NEW.id)) FROM org o WHERE o.id = NEW.org_id_client AND NEW.sum > o.max_limit ); END

Удобное написание логики и запрет динамического SQL в триггерах

Например, для позиций документа нам необходимо:

  • проверять, закрыт ли документ
  • при вставке позиции, если цена NULL, то определить цену по клиенту с помощью функции get_price
  • денормализовывать сумму документа в мастер таблице
Читайте также:  Рейтинг компаний по продаже бизнеса

Как я решил эту проблему? Я создал триггеры, которые:

  • в каждом BEFORE триггере создают MEMORY TEMPORARY TABLE с предопределённым именем _tmp_trg с одноимёнными столбцами и префиксами new_, old_ и полями time и type
  • поле time — время выполнения триггера B — BEFORE, A — AFTER
  • поле type — DML операция, I — INSERT, U — UPDATE, D — DELETE
  • вставляем текущие значения в триггере NEW. и OLD. в соответствующие поля
  • вызывается процедура _trg_proc
  • для BEFORE INSERT/UPDATE триггеров считываем обратно в переменные NEW. значения из соответствующих полей
  • удаляем данные из временной таблицы, в AFTER триггере DROP TEMPORARY TABLE

Мой генератор триггеров

Вот какой код нам выдаст генератор:

SHOW CREATE TABLE doc_pos; SELECT generate_triggers(‘doc_pos’);
Результат генератора триггеров

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

Запрос на проверку триггеров

SELECT DISTINCT CONCAT(EVENT_OBJECT_TABLE, ») msg FROM ( SELECT EVENT_OBJECT_TABLE , CONCAT(SUBSTRING_INDEX(gen_trg, ‘n’, 2), ‘n’, T.ACTION_STATEMENT, ‘$’, ‘n’) ACTION_STATEMENT , gen_trg gen_trg FROM ( SELECT T.ACTION_STATEMENT ACTION_STATEMENT , generate_trigger(T.EVENT_OBJECT_TABLE, T.ACTION_TIMING, T.EVENT_MANIPULATION) gen_trg , T.EVENT_OBJECT_TABLE FROM INFORMATION_SCHEMA.TRIGGERS T WHERE T.TRIGGER_SCHEMA = DATABASE() ) T ) T WHERE T.ACTION_STATEMENT != T.gen_trg

Что в итоге получаем? Единую точку входа для всех изменений, которые делают триггеры — _trg_proc

Теперь перепишем наш код под новую систему:

— Триггер для doc_pos DROP PROCEDURE IF EXISTS doc_pos_trg_proc$ CREATE PROCEDURE doc_pos_trg_proc() BEGIN DECLARE msg TEXT; — Документ закрыт. Изменения запрещены. SET msg := (SELECT raise_error(CONCAT(‘Документ закрыт (id = ‘ , d.id, ‘).

Изменения запрещены.’)) FROM doc_pos_tmp_trg dp INNER JOIN docs d ON d.id IN (dp.new_doc_id, dp.old_doc_id) WHERE d.closed = 1 AND dp.time = ‘B’ ); — Подставляем цену UPDATE doc_pos_tmp_trg INNER JOIN docs ON doc_pos_tmp_trg.new_doc_id = docs.id SET dp.new_price = get_price(dp.new_material_id, d.org_id_client) WHERE dp.time = ‘B’ AND dp.type = ‘I’; — Денормализация суммы UPDATE docs INNER JOIN doc_pos_tmp_trg ON docs.id IN (doc_pos_tmp_trg.new_doc_id, doc_pos_tmp_trg.old_doc_id) SET sum = IFNULL(docs.sum, 0) — CASE WHEN doc_pos_tmp_trg.old_doc_id = id THEN IFNULL(doc_pos_tmp_trg.old_kol * doc_pos_tmp_trg.old_price, 0) ELSE 0 END + CASE WHEN doc_pos_tmp_trg.new_doc_id = id THEN IFNULL(doc_pos_tmp_trg.new_kol * doc_pos_tmp_trg.new_price, 0) ELSE 0 END WHERE doc_pos_tmp_trg.time = ‘A’; END$

Кода стало меньше, он весь в одном месте и он не дублируется! Такой код поддерживать очень легко.

Хочу пояснить несколько моментов по реализации:

    такой подход вместо нативных триггеров, как в первом варианте даёт некоторый оверхед.

На тестовых данных, практически без «полезной» нагрузки 5000 строк вставляется ~1.8с,
в моём случае 5000 строк ~5.9с. Если вынести создание TEMPORARY TABLE и создать
перманетную таблицу и слегка оптимизировать триггер удалось достичь результата 5000 за 3.6c.

Но повторюсь, это вхолостую. В реальном коде доля затрат на создание и вставку данных в TEMPORARY TABLE не будет превышать 20%

Много тестовых запросов

Отсутствие AFTER STATEMENT TRIGGER

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

Например, при изменении статуса (атрибута) документа, необходимо создать один или цепочку дочерних документов. При изменении ветки nested sets деревьев, необходимо пересчитать left и right.

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

В идеале хотелось бы написать такой код:

Но в триггере запрещено менять ту же таблицу. Я решил эту проблему так:

CREATE TABLE `recursive_sql` ( `id` int(11) NOT NULL AUTO_INCREMENT, `sql_text` text NOT NULL, `pid` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `pid` (`pid`) )
CALL recursive_sql()

Лишние вызовы не дают практически никакой дополнительной нагрузки.

Вот тесты recursive_sql

DELIMITER $ DROP PROCEDURE IF EXISTS recursive_sql_speed_test$ CREATE PROCEDURE recursive_sql_speed_test() BEGIN declare x int unsigned default 0; WHILE x

Каждый вызов ~0.1 мс.

DROP PROCEDURE IF EXISTS doc_pos_trg_proc$ CREATE PROCEDURE doc_pos_trg_proc() BEGIN — . INSERT INTO recursive_sql (sql_text) SELECT CONCAT(‘UPDATE doc_pos SET kol = ‘ , (doc_pos.kol — IFNULL(doc_pos_tmp_trg.new_kol, 0) + IFNULL(doc_pos_tmp_trg.old_kol, 0)) , ‘ WHERE A’ ; END$

Итого

Получившийся инструментарий позволяет описывать БЛ на уровне БД наименьшим количеством кода, с максимальной производительностью и эффективностью.

Источник: habr.com

Рейтинг
( Пока оценок нет )
Загрузка ...
Бизнес для женщин