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

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.

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.

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.

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.

Related

Generate sales report using MySql and Php

I m looking for advices in report generating per client, per product, per salesman, in a given period, using mysql and php. I m currently working on an application with sales and i was wondering what is the best way to structure this function. I m planning on hosting in a shared hosting server, so i m worried about efficiency. So here are the two ways i m looking things:
1- add entries of sales then once a user specifies a period of the sales report, executing a mysql query that will sum up everything then feed it to the php to arrange it a send response back
2- fetch all entries then calculate sums and everything in php
3- on every sales, add the totals on a report table for each day, then another table for each client, then for each product then for each salesman. And once a user requires a report, fetch the totals in each table and sum up the totals (the problem here is if a sale has been edited or deleted, i have to re-adjust the totals for all tables)
I m looking for suggestions for the best way to do this, also all the data are on the same table and segregated by user_id so for example if there are 1000 users with 1000 sales, the table sales will have 1000000 rows with corresponding user_id.
Thanks in advance

Which is more efficient performance-wise price mysql table for size/product or price in csv format in a text column

Consider an example of a fabric store where customers can order any combination of sizes and the prices differ every 10cm. e.g. 100x100 = $100 110x100 = $110 110x110 = $140 ..etc (there is no pattern, price not per XXcm)
Would it be better to store a table of prices in a csv format in products table as a text column for each product and then php code interprets the table based on the size selected? or store them in a separate mysql table one for entry for each product and size combination?
My concern for text column is it increases the product table size greatly, but not sure if I make the prices as separate table would be much better.
Storing the prices in a separate table sounds like the most relationally sound option, if there is really no algorithm by which you can find out the price, like say by square cm for example.
An extra table gives you much better flexibility if you need to bulk change things. Also you can query the different sizes easily and compare them.
I would keep the width and height and price in separate columns in that table.
An INNER JOIN is also really not that expensive for a database if you use the right indexes/keys on the tables. So the main use - query a fabric for all available prices is comparatively cheap.

How to sort a MySQL query by a mathematical relation between multiple columns in multiple tables

I am developing a MySQL DB for an online jewelry shopping website. Every jewelry may have different stones, metals, … Their net prices will change everyday (e.g., every ounce of gold may be X today and Y tomorrow). Product A may be more expensive than B today, but cheaper tomorrow. Every time a user requests the products list, the website automatically calculates the price of every product.
How can users sort products by its price, considering that net price is not stored in a field in DB and every time will be calculated (using weights, net fees, …) by a function named func($id), where $id is the id of the product.
An illustration to the question:
Assuming you have all of your data in your DB, there is no reason you can't ORDER BY any kind of expression you want.
If, say, you had a table products (pid, name), productContents (pid, type, amount), and contentPrices (type, price), you could run something like
SELECT a.name, SUM(b.amount*c.price) AS totalPrice FROM products a
LEFT JOIN productContent b ON a.pid = b.pid
LEFT JOIN contentPrices c ON b.type=c.type
ORDER BY totalPrice DESC;
EDIT to work with the updated question schema:
SELECT p.title, p.metal_weight*m.fee + p.stone_weight*s.fee AS totalPrice FROM Products p
LEFT JOIN Metals m ON p.metal_id=m.id
LEFT JOIN Stones s ON p.stone_id=s.id
ORDER BY totalPrice DESC;
I would do it in two ways, but first of all, if you want a good performance, store the price value in a Field. I don't think it changes every second, does it?
So first, if you can make your Price calculator function in MySQL, create a Stored Procedure for it and run this on the table periodically (for example every day at 00:00) and store the value in a field. You can find how to schedule an event in MySQL here.
If you can't do this, stay with php, but it also should called periodically to calculate everything. You can do it with cron on Unix (if you have the rights). This can be scheduled on a remote server too, so if you have somewhere a server where you can use cron, simply call the updater.php with wget, for example:
wget -c http://jewerly-shop.com/update_prices.php && rm -rf update_prices.php
But if you need further help, we need more info (like the function that does the calculating and the table structures).

How to design database for tracking orders with set max items

I've got the following scenario:
In an order tracking system where sales reps track their orders:
Each order can have a total of 4 product types(commissionable buckets) and each bucket can 1 or none products of that product type.
So at max an order can have 4 products(one of each of the possible types)
The information I need to track for each of the four product types is the same: product-sold-id(fk), status, product-given-id(fk)
What would be the advantages of adding 12 columns to the order table: 3 for each product type like product-type1-sold-id, product-type1-status, product-type1-given-id..... Etc?
This would be easiest but my issue with this is that most orders would only have 2 or 3 product types sold which would result in 3 null fields per product type not sold. With 100 reps # 10 orders per day this would result in a lot of null fields.
I am leaning toward have an order items table with an order-id(fk) to cut down on all the null fields.
The issue with this approach would be that for one, I would have to add a level of complexity to either my client code to handle nested data, or my server code would have to flatten the data when requested and also be able to parse and save the flat data when sent modified from the client. Also with 100 reps#10 orders per day with 1-4 product types per order this would result in 1000-4000 rows added to the order items table per day. Is there any way of overcoming this?
Any advice would be appreciated. I am using extjs clientside, php serverside, with MySQL as my db.
Build a standard order data model, and put your application logic where it belongs, i.e. in the application layer (or maybe db sprocs).
What if your order maximum of 4 changes?
You should keep orders, products, and product types in their own tables. Then, you will use foreign keys to LEFT JOIN the tables together when returning results in PHP. This will allow you to scale the application with minimal effort. For example, adding up to five types to a product will require no changes to the database structure. Everything will be handled within the PHP code when limiting product types and products per order.
The foreign key is simply just a pointer to another table. For example, the table product_types should have a column named product_id for the primary key of the related product. The same works for products within an order. This is a very common way to represent one-to-many or many-to-many relationships.
For a code example, this is how you would return an order with the products.
SELECT * FROM products AS p
LEFT OUTER JOIN orders AS o ON o.id=p.order_id
WHERE o.id=1234;

Display rows based on $array

I have a table in postgres called workorders. In it are various headings. The ones I am interested in are labor, date_out and ident. This table ties up with wo_parts (workorder parts). In this table are the headings I am interested in, part and workorder. Both are integers. (part auto number) The final table is part2vendor and the headings are retail and cost. Right, basically what happens is.....I create a workorder (invoice). This calls a part from part2vendor. I enter it and invoice it off. In workorder a row is created and saved. It is given an ident. In wo_parts, the part i used is recorded as well as workorder number and qty used. What I want to do is create a report in php that pools all this info on one page. IE. if i choose dates 2009-10-01 to 2009-10-31 it will pull all workorders in this range and tell me the total labour sold and then the PROFIT (retail less cost) of the parts I sold, using these 3 tables. I hope i have explained as clear as possible. any questions please ask me. Thank you very much for your time.
You will want to read up on SQL - keywords to look for include "aggregate", "SUM" and "GROUP BY".
You query will look something like (but this will certainly need correcting):
SELECT
SUM(wo.labor) AS tot_labor,
SUM(p2v.cost - p2v.retail) AS tot_profit
FROM
workorders AS wo
JOIN wo_parts AS wp ON wo.ident=wp.ident [?]
JOIN part2vendor AS p2v ON ...something...
WHERE
date_out BETWEEN '2009-10-01'::date AND '2009-10-31'::date;

Categories