Дизайн бази даних: Розрахунок залишку на рахунку


76

Як створити базу даних для розрахунку залишку на рахунку?

1) Наразі я обчислюю залишок на рахунку з таблиці транзакцій. У моїй таблиці транзакцій є "опис", "сума" тощо.

Потім я б склав усі значення "суми", і це дозволило б визначити залишок на рахунку користувача.


Я показав це своєму другові, і він сказав, що це не є гарним рішенням, коли моя база даних росте, вона буде гальмуватися ???? Він сказав, що я повинен створити окрему таблицю для зберігання розрахованого залишку на рахунку. Якщо це було зроблено, мені доведеться вести дві таблиці, і, якщо це ризиковано, таблиця балансу рахунку може вийти з ладу.

Будь-яка пропозиція?

РЕДАКТУВАТИ : ВАРІАНТ 2: чи слід додати додатковий стовпець до моїх таблиць транзакцій "Баланс". тепер мені не потрібно переглядати багато рядків даних, щоб виконати мій розрахунок.

Приклад: Джон купує кредит у 100 доларів, він боргує 60 доларів, а потім додає 200 доларів.

Сума $ 100, залишок $ 100.

Сума - 60 доларів, залишок - 40 доларів.

Сума $ 200, залишок $ 240.


1
Які очікувані обсяги транзакцій?
Patrick Honorez

wtf - це те питання, де кожен отримує негативні бали ??
Patrick Honorez

не маю ідеї iDevelop, я нікому не дав позитивного чи негативного бачення :), зараз я збентежений, один хлопець каже так, інший каже ні. Що відбувається! ??
001

7
Проблема поля Баланс у таблиці "Транзакції" полягає в тому, що ви не тільки створюєте транзитивну залежність на рівні рядків, яка не є "Звичайною", але ви додаєте транзитивну залежність на рівні стовпця, що буде головним болем, якщо у вас виникнуть проблеми (збій тригера або інший). Моя порада полягає в тому, щоб записати свою нормалізовану структуру, потім написати кожен "варіант використання", який ви плануєте мати, обговорити їх з іншими, а потім переглянути свою структуру з урахуванням ваших випадків використання, щоб побачити, чи потрібна якась денормалізація. У будь-якому випадку, етап проектування є вирішальним, не поспішаючи витрачати час!
Patrick Honorez

6
"транзакції ніколи не видаляються з попередніх років" ... Я б хотів сказати про це двічі. Ви можете подумати про переміщення старих транзакцій до архівної таблиці через деякий час, + створити спеціальний тип транзакцій (InitialBalance) в активній таблиці. Це може бути частиною щорічного процесу (або будь-яких відповідних часових рамок). І ви повинні включити цей пункт у свої "
Приклади

Відповіді:


72

Вікова проблема, яка ніколи не вирішувалася елегантно.

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

Правильний шлях:

  • У таблиці переміщення є транзакція "відкриття залишку" для кожного рахунку. Це вам знадобиться через кілька років, коли вам потрібно буде перемістити старі рухи з таблиці активних рухів до таблиці історії.
  • Суб’єкт рахунку має поле балансу
  • У таблиці руху є тригер, який оновлює залишки на рахунках для зарахованих та дебетованих рахунків. Очевидно, що він має контроль над зобов'язаннями. Якщо у вас не може бути тригера, тоді повинен бути унікальний модуль, який записує рухи під контролем зобов’язань
  • У вас є програма "мережа безпеки", яку ви можете запускати в автономному режимі, яка повторно обчислює всі залишки та відображає (і, за бажанням, коригує) помилкові залишки. Це дуже корисно для тестування.

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

Зверніть увагу, що ці методи застосовуються як до готівки, так і до цінних паперів.

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


4
"Особисто я віддаю перевагу кредитному полі, дебетовому полі та підписаній сумі, що значно полегшує відстеження.", Чому б просто не мати 1 поля "Сума" зі знаком?
001

9
@ 001 Кожна транзакція містить 3 поля. 1 / Ідентифікатор рахунку, який списано. 2 / Ідентифікатор рахунку, що зараховується. 3 / Сума, позитивна = переказ від кредитора до боржника, негативна = переказ від боржника до кредитора (зазвичай сторнування). Пропоную прочитати en.wikipedia.org/wiki/Double-entry_bookkeeping_system
smirkingman

1
@ 001 Це 2 рухи. 1 / X зараховує 5000 до Y. 2 / X платить 500 податку до Z. Це має бути зроблено таким чином, оскільки пізніше хтось попросить "показати всі платежі на податковий рахунок Z", а ви не хочете, щоб 5000 в та відповідь. Я справді пропоную вам трохи почитати фонового режиму з ведення книг, ви знайдете це дуже корисно
smirkingman

1
@ 001 Як я вже пропонував вище, вам слід навчитися основам бухгалтерського обліку подвійних записів
smirkingman

6
Ця відповідь неправильна, залежить від коду та громіздка. Для методу, який (a) відповідає вимогам аудиту та законодавству, (b) не вимагає активаторів; автономні захисні мережі; дублікати стовпців; дубльовані дані та (c) добре працює незалежно від сукупності таблиць, подивіться на цю відповідь .
PerformanceDBA

4

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

Усі оновлення цієї пари таблиць повинні бути в транзакції та забезпечувати, щоб або все залишалося синхронізованим (і рахунок ніколи не перевищував ліміт), або транзакція відкочувалася назад. Як додатковий захід, ви можете запускати аудиторські запити, які періодично перевіряють це.


Скажіть, що людина прийде перевірити вашу бухгалтерію. Ви здивуєтесь, як довго вони сміються. Кожна транзакція на рахунок повинна бути пронумерована (для підтримання замовлення), і ви можете просто покласти туди новий баланс рахунку. Насправді немає потреби в другій таблиці. Без покарання за продуктивність. Дублі, до речі, як ведеться бухгалтерія. Про що це все.
TomTom

@TomTom: тепер я бачу вашу думку. Ваша ідея гарна. Шкода, що це не було чітко сказано у вашій відповіді, і дуже погано, що ваші відповіді виглядають такими агресивними!
Патрік Гонорес

1
@TomTom, формулювання Марсело є дещо неоднозначним, але ваші коментарі щодо аудиту не є корисними і не правильними. Аудит полягає у встановленні правильності та мало спільного з оптимізацією швидкості. Марсело трохи неточний, оскільки він говорить про "поточний баланс", де насправді більшість фінансових систем зберігатимуть залишки відповідно до рахунків та інших аналітичних вимірів, підсумованих за певними датами деталізації. Ваша ідея підтримувати поточний баланс на рівні трансакції марна для будь-якого звіту, який потребував би фільтрації транзакцій за будь-чим, крім атрибута, який відповідає порядку бронювання.
Необгрунтовано

4

Це дизайн бази даних, який я отримав лише з однією таблицею для простого зберігання історії операцій / транзакцій. В даний час працює як шарм над багатьма невеликими проектами.

Це не замінює конкретний дизайн. Це загальне рішення, яке може відповідати більшості програм.

id : int стандартний ідентифікатор рядка

тип_ операції : тип операції int. платити, стягувати, відсотки тощо

тип_джерела : int, звідки триває операція. цільова таблиця або категорія: користувач, банк, постачальник тощо

source_id : int id джерела в базі даних

тип_цілі : int до того, до якої операції застосовується. цільова таблиця або категорія: користувач, банк, постачальник тощо

цільовий_ід : int id цілі в базі даних

сума : десяткова (19,2 підписана) цінова ціна додатна або від’ємна до підсумовується

баланс : десятковий (19,2 підписаних) результуючий баланс

зайве_значення_а : десятковий (19,2 підписаних) [це був найбільш універсальний варіант без використання рядкового зберігання], ви можете зберегти додаткове число: процентний відсоток, знижка, зменшення тощо.

created_at : часу

Для source_type і target_type було б краще використовувати enum або таблиці appart.

Якщо ви хочете отримати певний баланс, ви можете просто здійснити запит останньої операції, відсортованої за спадною межею create_at до 1.

Для кращої роботи рекомендується зберігати поточний баланс у необхідному цільовому об'єкті.


2

Загальним рішенням цієї проблеми є підтримка (скажімо) щомісячного залишку на відкритті в схемі знімків. Розрахунок поточного залишку можна здійснити, додавши дані про трансакції за місяць до щомісячного залишку. Цей підхід часто застосовується в пакетах рахунків, особливо там, де у вас можуть бути конвертація валюти та переоцінка.

Якщо у вас є проблеми з обсягом даних, ви можете архівувати застарілі залишки.

Крім того, залишки можуть бути корисними для складання звітності, якщо у вас немає спеціального зовнішнього сховища даних або засобів звітного управління в системі.


1

Звичайно, вам потрібно зберігати поточний баланс у кожному рядку, інакше це занадто повільно. Щоб спростити розробку, ви можете використовувати обмеження, так що вам не потрібні тригери та періодичні перевірки цілісності даних. Я описав це тут Денормалізація для забезпечення ділових правил: Запуск підсумків


2
Я бачу, що вам подобаються ваші файлові картотеки із залитим у них бетоном.
PerformanceDBA

0

Ваш друг помиляється, а ви маєте рацію, і я б порадив вам не змінювати речі зараз.
Якщо ваш db коли-небудь буде повільним через це, і після того, як ви перевірили всі інші (належне індексування), деяка денормалізація може бути корисною.
Потім ви можете помістити поле BalanceAtStartOfYear у таблицю Рахунки та підсумувати лише записи цього року (або будь-який подібний підхід).
Але я б точно не рекомендував такий підхід заздалегідь.


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

1
@TomTom. Звичайно, вони це роблять, якщо Транзакції здійснюються згідно зі Стандартами бухгалтерського обліку (Ваші ні), у цьому випадку "ad hoc" є хибним. Крім того, дублювати CurrentBalance у кожному окремому рядку просто дурно: коли потрібно зробити коригування, маси рядків повинні бути оновлені.
PerformanceDBA

0

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

  1. Створіть функцію запуску в таблиці транзакцій, яка буде викликана лише після оновлення або вставки.

  2. Створіть стовпець із назвою у головній таблиці іменування рахунків Відкриття балансу.

  3. збережіть свій початковий баланс у масиві у стовпці початкового балансу в головній таблиці.

  4. вам навіть не потрібно використовувати мову на стороні сервера, використовуйте цей масив магазину, просто ви можете використовувати функції масиву бази даних, такі як доступні в PostgreSQL.

  5. коли ви хочете перерахувати залишковий баланс у масиві, просто згрупуйте таблицю транзакцій за допомогою функції масиву та оновіть цілі дані в головній таблиці.

Я зробив це в PostgreSQL і працюю чудово.

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


привіт, мені незрозуміло з номером 3. "початковий баланс" знаходиться в одній таблиці транзакцій або в окремій таблиці?
Axil

0

Мій підхід полягає в тому, щоб зберігати дебети в стовпці дебету, кредит у стовпці кредиту і при отриманні даних створювати два масиви, дебет і кредитний масив. Потім продовжуйте додавати вибрані дані до масиву і робіть це для python:

def real_insert(arr, index, value):
    try:
        arr[index] = value
    except IndexError:
        arr.insert(index, value)


def add_array(args=[], index=0):
    total = 0
    if index:
        for a in args[: index]:
            total += a
    else:
        for a in args:
            total += a
    return total

тоді

for n in range(0, len(array), 1):
    self.store.clear()
    self.store.append([str(array[n][4])])
    real_insert(self.row_id, n, array[n][0])
    real_insert(self.debit_array, n, array[n][7])
    real_insert(self.credit_array, n, array[n][8])
    if self.category in ["Assets", "Expenses"]:
        balance = add_array(self.debit_array) - add_array(self.credit_array)
    else:
        balance = add_array(self.credit_array) - add_array(self.debit_array)

-3

Проста відповідь: виконайте всі три.

Зберігати поточний баланс; і в кожній транзакції зберігайте рух і знімок поточного залишку на той момент часу. Це дало б щось додаткове для узгодження в будь-якому аудиті.

Я ніколи не працював над основними банківськими системами, але я працював над системами управління інвестиціями, і на моєму досвіді це так робиться.


1
Це дає щось додаткове для узгодження під час аудиту. Що допомагає перевірити програмне забезпечення (з точки зору правильних розрахунків та перевірки видалення транзакцій; і не є вимогою = єдиний спосіб це зробити), який, де я працюю, є частиною ІТ-аудиту; але це не пов'язано безпосередньо з аудитом бухгалтерського обліку (ІТ-аудит - це лише частина фінансового аудиту).
Необгрунтовано

Правильно, але малоймовірно, що велика взаємодіюча система може зіткнутися з ситуаціями, коли певний процес (наприклад, овертайт) створює аномалії; денормалізація, допоможе забезпечити їх відстеження.
Dog Ears

@Dog Ears, я розумію - отже, більшість ваших записів походять від обміну даними / взаємодії, і тоді ІТ-аудит є набагато сильнішою складовою фінансового аудиту. Цікаво. Проте я дотримуюсь своєї позиції: це додаткове значення контролю, яке існувало б для всіх транзакцій. Якби я хотів перевірити процес обміну даними, я б поставив елементи керування, які концентруються на ньому (використовуючи якусь контрольну суму обміну даними: обчислюється ініціатором і просто отримується, розраховується на отриманих даних у отриманому форматі - перевіряє цілісність передачі , розраховане на імпортовані дані - перевіряє процедуру імпорту)
Непричина

1
@smirkingman на користь спільноти, що жахливо в моїй пораді, адже, чим вона відрізняється від вашої поради? Здається, ми в основному пропонували одне і те ж?
Dog Ears

3
Вуха @dogs жахливість зберігає рівновагу в кожному русі. Більше дубльованих даних і кошмар, який можна виправити, коли щось піде не так.
посмішка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.