ER Design Project: Banking System
A banking database with a generalisation hierarchy for account types, a many to many customer to account relationship, and a transaction model that never stores a balance it can compute.
-
DBMS Fundamentals
- Data, Information and Databases
- What a DBMS Is and Why It Exists
- File System versus DBMS
- Advantages and Limitations of a DBMS
- Database Users and the Role of the DBA
- Three Level Architecture and Data Abstraction
- Logical and Physical Data Independence
- Schema, Instance and Metadata
- Database Applications and the Database System Environment
- Database Architecture
- Data Models
-
ER Model
- Entities, Entity Sets and Entity Types
- Types of Attributes in the ER Model
- Keys in the ER Model
- Relationships, Relationship Sets and Degree
- Cardinality and Participation Constraints
- Strong and Weak Entities
- Drawing and Reading ER Diagrams
- Extended ER: Generalisation, Specialisation and Aggregation
- Converting an ER Diagram into Relational Tables
- ER Design Projects
- Relational Model
- Relational Algebra
- Functional Dependencies
-
Normalisation
- Why Normalisation Exists: Anomalies and Redundancy
- First Normal Form
- Second Normal Form and Partial Dependency
- Third Normal Form and Transitive Dependency
- BCNF and BCNF Decomposition
- 4NF, 5NF, Multivalued and Join Dependencies
- Lossless Decomposition and Dependency Preservation
- Complete Worked Normalisation: Unnormalised to BCNF
- Denormalisation and When to Use It
Requirement
A bank has branches, each with a code, a name, a city and total assets. Customers have a customer number, a name, an address and a contact number, and are registered at exactly one home branch.
Accounts have a number, an opening date and a balance, and are held at exactly one branch. An account may be a savings account, with an interest rate and a minimum balance, or a current account, with an overdraft limit. Every account is one or the other and never both.
An account may be jointly held by several customers, and a customer may hold several accounts. The date a customer was added to an account is recorded.
Every transaction on an account records a date and time, a type of either deposit or withdrawal, and an amount. A transfer is recorded as two transactions.
Loans have a number, an amount and a type, are made by one branch, and may be taken jointly by several customers. Loan repayments record a date and an amount, and a repayment has no meaning without its loan.
Step 1 — Entities
| Entity | Attributes | Key | Type |
|---|---|---|---|
| BRANCH | branch_code, branch_name, city, assets | branch_code | Strong |
| CUSTOMER | cust_no, name, address (composite), contact | cust_no | Strong |
| ACCOUNT | acc_no, opened_on, balance | acc_no | Strong, superclass |
| SAVINGS | interest_rate, min_balance | acc_no | Subclass |
| CURRENT | overdraft_limit | acc_no | Subclass |
| TRANSACTION | txn_id, txn_datetime, txn_type, amount | txn_id | Strong |
| LOAN | loan_no, amount, loan_type | loan_no | Strong |
| REPAYMENT | payment_no (partial), paid_on, amount | loan_no + payment_no | Weak |
Step 2 — The generalisation
+-------------+
| ACCOUNT | acc_no, opened_on, balance
+------+------+
|
/
/ d disjoint
+-----+
======= TOTAL (double line)
/
+----------+ +----------+
| SAVINGS | | CURRENT |
+----------+ +----------+
interest_rate overdraft_limit
min_balance
DISJOINT because an account is one or the other, never both.
TOTAL because every account must be one of them.Step 3 — Relationships
| Relationship | Between | Ratio | Participation | Attributes |
|---|---|---|---|---|
| HOME_BRANCH | CUSTOMER — BRANCH | N:1 | Customer total | — |
| HELD_AT | ACCOUNT — BRANCH | N:1 | Account total | — |
| HOLDS | CUSTOMER — ACCOUNT | M:N | Account total, customer partial | added_on |
| ON_ACCOUNT | TRANSACTION — ACCOUNT | N:1 | Transaction total | — |
| GRANTED_BY | LOAN — BRANCH | N:1 | Loan total | — |
| BORROWS | CUSTOMER — LOAN | M:N | Loan total, customer partial | — |
| HAS_REPAYMENT | LOAN — REPAYMENT | 1:N identifying | Repayment total | — |
Account participation in HOLDS is total: an account must have at least one holder. That is a rule the schema cannot express with a foreign key alone, and it is noted here so it can be enforced deliberately.
Step 4 — The relational schema
branches ( branch_code PK, branch_name NOT NULL, city, assets )
customers ( cust_no PK, cust_name NOT NULL
, street, city, pincode, contact
, home_branch NOT NULL -> branches )
accounts ( acc_no PK
, opened_on NOT NULL
, balance NOT NULL DEFAULT 0
, acc_type NOT NULL -- savings or current
, branch_code NOT NULL -> branches )
savings_accounts ( acc_no PK -> accounts ON DELETE CASCADE
, interest_rate NOT NULL
, min_balance NOT NULL )
current_accounts ( acc_no PK -> accounts ON DELETE CASCADE
, overdraft_limit NOT NULL )
account_holders ( acc_no -> accounts
, cust_no -> customers
, added_on NOT NULL
, PK ( acc_no, cust_no ) )
transactions ( txn_id PK
, acc_no NOT NULL -> accounts
, txn_datetime NOT NULL
, txn_type NOT NULL -- deposit or withdrawal
, amount NOT NULL )
loans ( loan_no PK, amount NOT NULL, loan_type
, branch_code NOT NULL -> branches )
borrowers ( loan_no -> loans, cust_no -> customers
, PK ( loan_no, cust_no ) )
repayments ( loan_no -> loans ON DELETE CASCADE
, payment_no
, paid_on NOT NULL, amount NOT NULL
, PK ( loan_no, payment_no ) )
10 tables.Step 5 — The balance question
The requirement lists balance as an attribute of ACCOUNT, and it also lists every transaction. That is a stored value which is also derivable, and it deserves a deliberate decision.
| Derive it from transactions | Store it on the account | |
|---|---|---|
| Correctness | Always exactly right | Right only if every update is correct |
| Cost of a read | Sum every transaction — grows forever | One column read |
| Risk | Slow on old accounts | Drift: the stored figure disagrees with the history |
The banking answer is to store it and protect it: update the balance and insert the transaction inside one transaction, so they cannot disagree, and reconcile periodically. This is the first place in the path where atomicity is not a theoretical benefit but the entire design.
Never let an application update the balance and insert the transaction as two separate operations. A crash between them creates money or destroys it. Phase 9 explains exactly why the two statements must be one atomic unit.
Verification questions
- What is the balance of an account, and does the transaction history agree with it?
- Which customers jointly hold a given account, and since when?
- Which accounts at a branch are savings accounts below their minimum balance?
- What is the outstanding amount on a loan?
- Which customers hold both an account and a loan at the same branch?
Common mistakes
- Modelling the customer to account relationship as 1:N, which makes joint accounts impossible.
- Storing a single
transactionrow for a transfer instead of two, losing which account each side affected. - Putting
interest_rateon every account and leaving it null for current accounts. - Forgetting that repayments are weak, and giving them an independent key.
- Deleting closed accounts. Banking records are retained; use a status instead.
Practice
- Add a transfer entity that links two transactions and enforces that the amounts match.
- Write the constraint needed to enforce that every account has at least one holder, and say why a foreign key cannot do it.
- Design the reconciliation query that finds accounts whose stored balance disagrees with their transaction history.
Conclusion
Ten tables, one disjoint total generalisation, two many to many relationships and one weak entity. The idea worth carrying forward is the balance decision: when a value is both stored and derivable, the design must say how the two are kept in agreement.