简体   繁体   中英

What is the proper way of storing calculated amounts in database?

I have 3 tables ( customers , invoices , invoice_items ).

A customer can have multiple invoices and an invoice can have multiple items.

This is my current schema:

customers

id
name
timestamps

invoices

id
customer_id
timestamps

invoice_items

id
invoice_id
price
timestamps

Note: timestamps are created_at and updated_at fields. No need to worry.

as you can see, I am not storing the total amount of an invoice in the invoices table. I let the front-end calculate it.

Now, my client has 1000+ customers and they want to show all customers in the homepage along with the total sum of all invoices they have.

ID | Customer name | Total Invoice (sum of all invoices)

Right now I am using Laravel, so my query looks like this:

$customers = Customer::with('invoices.items')->get();

What's the best way to do this? Performance wise?

Should I just store the total amount of each invoice in the invoices table to avoid querying the items and calculate the sum? Something like:

invoices

id
customer_id
total
timestamps

Imagine a customer having thousand invoices and each invoice has multiple items. The query will be very slow and I have to calculate every thing in the front-end.

Also, when an invoice has been made, it can't be changed. So the total for an invoice is static.

It looks good apart from the timestamps. Can you not just use one timestamp column in your invoices table?

Why do you need the same column in all 3 tables?

Also, I'm sure you can calculate total in your php script. You don't need to store the total that you can calculate through a count() in php.

Ofcourse, when you add a total column on invoice table then you should get a boost in the performance, as you will not have to go through all the invoice_items and add them up. This is good if you need to add items to the invoice only at once and do not need to be updated later. This way, you will need to add the total while generating the invoice at once.

Adding total column in invoice table will be definitely good for performance and it will also help fetch result through eloquent. However, if performance is not something important here, we can do it without it. Your SQL query should be like (might need little adjustment)

SELECT c.*, i.*, sum(ii. price) as total_invoice_price
FROM customers c
INNER JOIN invoices i on i.customer_id = c.id
INNER JOIN invoice_items ii on ii.invoice_id = i.id
GROUP BY i.id

Once you have expected result through SQL, you can run it using DB::table in laravel.

This is classic case of WORM (Write Once Read Many).

Lets look into pros and cons of both the approaches:

  • Case 1: Not storing the total in invoices table

    • Pros: Simple and easy. No data redundancy.
    • Cons: Slow performance and consuming resources(memory and cpu) every time you compute the total
  • Case 2: Storing the total in invoice table

    • Pros: Better performance because "complex" invoice total computation happens only once and subsequent reads are faster
    • Cons: Data redundancy and requires some extra storage

Now if you look into the cons of both the approaches, case 2 is obviously better because extra storage can be bought easily but performance can't be even if you increase RAM and CPU upto some extent.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM