Лучший подход для отслеживания поля Amount в таблице Invoice при изменении элементов InvoiceItem? - PullRequest
0 голосов
/ 04 октября 2018

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

Customer
 - Id
 - Name

Invoice
 - Id
 - CreatedOn
 - PaidOn
 - CustomerId

InvoiceItem
 - Id
 - Amount
 - InvoiceId

Обычно я получаю все данные, используя Entity Framework, и вычисляю все в моей службе C # (или даже выполняю вычисления на SQL Server) примерно так:

var amountOwed = Invoice.Where(i => i.CustomerId == customer.Id)
                        .SelectMany(i => i.InvoiceItems)
                        .Select(ii => ii.Amount)
                        .Sum()

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

Для этого сценария я думал о добавлении поля Amount в мою таблицу Invoice и, возможно, AmountOwed в мою таблицу Customer, которая будетобновляться или заполняться с помощью InvoiceService всякий раз, когда я вставляю / обновляю / удаляю InvoiceItem.Это должно быть достаточно безопасно и сделать запрос к отчету намного быстрее.

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

Мой вопрос:

Как добавитьтриггер для обновления всех родительских таблиц при каждом изменении InvoiceItem?

И, по вашему опыту, это лучшее (безопасное, менее подверженное ошибкам) ​​решение этой проблемы или я что-то упустил?

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

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

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

Поскольку этот процесс включает в себя изменения в таблице элементов, вам необходимо написать триггеры для обработки всех трех типов операторов dml - вставка, обновление и удаление.Напишите триггер для каждого, чтобы упростить ваше обучение и отладку.Триггеры имеют доступ к специальным таблицам - узнайте о них.И узнайте о ложном предположении, что триггер работает с одной строкой - это не так.Триггеры должны быть написаны для правильной работы, если затронуты 0 (да, ноль), 1 или много строк.

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

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

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

0 голосов
/ 04 октября 2018

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

В большинстве случаев вы можете легко заполнить свои данные отчетности через структуру сущностей или запрос SQL.Но если для этого требуется много объединений, вам следует рассмотреть возможность использования промежуточных таблиц.Потому что отчетность требует денормализации данных.Для заполнения промежуточных таблиц вы можете использовать задания SQL или другой механизм планирования (возможно, планировщик Azure).Таким образом, вам не нужно работать с большим количеством объединений, и ваши отчеты будут заполняться быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...