MySQL Database analyze problem in some table - mysql

Tables and relations
Orders
Products
OrderItem
Relation between "Orders" and "Products" is many to many, and "OrderItem" table connects them.
The Problem
When a product is ordered, the product has specific in-price, out-price, and discount. For example, Product A is added at in-price 100, out-price 120, and discount 0, and suppose later, admin changes one of these properties. Now, because the order has a reference to product A, there is a difference between the product details when it was ordered and the present.
I want the ability to be able to update product details. How we can solve this in relational databases.

You need both.
In Product, which is the current value, that changes with a whim.
And in OrderItem, which is what the Product was actually sold for.
This is not a duplicate.
Product (
product-code,
name,
in-price, -- current, changing
out-price,
)
OrderItem (
customer-code, -- FK to Customer
datetime,
product-code, -- FK to Product
in-price, -- actual, historic
out-price,
discount,
quantity
)

When you promise a particular price to a customer, that goes with the customer order as, say, promised_price. That is, you must (for business reasons) have a separate, potentially redundant, "price" to indicate this.
Then whatever the admin does to the in- and out-prices or discount does not impact promises already made.
If the price goes down (or up), you could choose to give the new price to a customer, but that must be a separate, deliberate, action to change the promised_price.
The overhead of having lots of prices floating around for each product is small enough not to worry about the space consumed. An approach like this helps you be an honest businessman, with auditable books.

Related

DB design for product pricing history for multiple suppliers

I am currently trying to design DB(mysql) structure for my project which is an online shop for wholesale company - I already created everything when it comes to products, it's multiple variants etc but I have problem with following which is price and historic data for multiple suppliers:
Please find below main assumptions for the project:
We are going to have several suppliers for products
Thanks to the above each product will have few different prices
We want to be able to have historic price data for each product with each supplier
Variant 1
At first I thought about adding 2 tables to my DB:
suppliers table: supplier_id, name
prices table: id, product_id, price_supplier1, price_supplier2, price_supplier3, timestamp
However in such example whenever we want to add another supplier we need to add row to the database (I am not a db expert but I guess that's not the best approach)
Variant 2
Another idea was just to have price table with following:
suppliers table: supplier_id, name
prices table: id, product_id, supplier_id, timestamp
However in this case if we have 5 suppliers we get 5 records created for 1 products every single day so let's imagine that we have only 1000 products and want to keep historic data for last 6 months - such table would grow very rapidly
So to summarize - which approach is better or maybe there is a different one that I could implement? Thanks a lot for any suggestions.
You should go with variant 2. It's best practice to avoid frequent table restructure, which you would have to do in variant 1 any time you add or remove a supplier (although MySQL is fairly fast at this in recent versions). Using a single column to identify the distinct supplier values is better. It also promotes query reuse when you don't have to worry about column values changing or being dropped altogether. Also, space shouldn't really be a concern. To give you an idea, if your prices table had 1,000,000 rows (6 months), it would be about 40-50M in size (assuming only a primary key index). MySQL also offers compression and partitioning to reduce storage as well, if that's really a concern.

How do I use lists in MySQL

How do I represent invoices into an MySQL table structure?
The invoice table has the following fields:
id
customer_id
services
tax
total_price
date
...
Now the services can contain one or more articles and, what makes it complicated, various prices. The actual price of a service is negotiated, there are seldom fixed prices because each customer has individual requirements. These items have to be listed on the invoice and the prices of each have to be listed and summarized + tax.
What is the best practice for this purpose? I want to make it normalized if possible, something more sophisticated than just saving an (serialized) array of service => price into the service field, if this is possible at all. Do I use a second table for each service + price and hold a list of IDs with foreign keys?
I think you should follow Has And Belongs To Many relationship. Fr that you use two more tables for this one is 'services' and other is 'invoice_services'
'services' table contain service info and its price etc.
'invoice_services' table will be the join table for 'invoice' and 'services' tables. 'invoice_services' table will contain 'service_id' and 'invoice_id'.
Then you can remove the column 'services' from 'invoice' table.
If you want to maintain negotiable price for each service, then you can add one more field 'custom_price' in 'invoice_services' table.
Then using joins you can fetch the relative data of any invoice.
Hope this may help :)

Can I create a table structure with dynamic columns in MySQL?

I've created a stock control database which contains two tables (actually more than two, but these are the two that are relevant to my question): Stock, and Receipts
I would like the link between the stock in the stock table,and the stock in the receipts table to be a little more clearer, this would be fine if a customer could only order one item of stock per receipt, as i'd simply have a StockID column and a Quantity column in the Recipts table, with the StockID column as an FK to the ID in the Stock table, however, the customer can make a receipt with any number of items of stock on it, which would mean i'd have to have a large number of columns in the Receipts table (i.e. StockID_1, Quantity_1, StockID_2, Quantity_2 etc.)
Is there a way around this (can you have like a dynamically expanding set of columns in MySQL) within MySQL, other than what i've done at the moment, which is to have an OrderContents column with the following structure (which isn't enforced by the database or anything) StockID1xQuantity,StockID2xQuantity and so on?
I would post an image of the DB structure, but I don't have enough repuation yet. My lecturer mentioned something about that it could be done, by normalising the database into 4th or 5th normal form?
I'd suggest having 3 tables:
Stock (StockID) + stock specific fields
Receipt (ReceiptID) + receipt specific fields.
StockReceipt (ReceiptID, StockID, Quantity) (could have a StockReceiptID, or use StockID+ReceiptID as Primary Key)
A solution including prices could look like:
Stock (StockID, Price)
PriceHistory (StockID, Price, Date) or (DateFrom, DateTo)
Receipt (ReceiptID, ReceiptDate)
StockReceipt (ReceiptID, StockID, Quantity)
That way you can calculate TotalStockReceiptPrice and TotalReceiptPrice for any receipt in the past.
I suspect this might be what you're looking for:
Stock (StockID, StockPrice)
Receipt (ReceiptID)
StockReceipt (ReceiptID, StockID, Quantity)
SELECT r.ReceiptID, SUM(s.StockPrice * sr.Quantity) AS ReceiptPrice
FROM Receipt r
INNER JOIN StockReceipt sr ON r.ReceiptID = sr.ReceiptID
INNER JOIN Stock s ON sr.StockID = s.StockID
GROUP BY r.ReceiptID
This is all very normalised (again, no idea to what normal form - 3rd?). However it only works if the StockPrice on the Stock record NEVER changes. As soon as it changes your ReceiptPrices would all reflect the new price instead of what the customer actually paid.
If the price can change, you'd need to either keep a price history table (ItemID, Price, DateTo, DateFrom) or record the StockPrice on the StockReceipt record (and then get rid of the JOIN to the Stock record in the above query and make it use sr.StockPrice instead of s.StockPrice)
To do the INSERT you posted below, you'd have to do:
INSERT INTO StockReceipts (ReceiptID, StockID, Quantity, TotalStockPrice)
SELECT 1, 99, 2, s.StockPrice
FROM Stock s
WHERE s.StockID = 99
However it's quite likely that whatever is issuing this receipt (and triggers the INSERT) already knows the price so could just insert the value.
No, relational databases do not allow dynamic columns. The definition of a relational table is that it has a header that name the columns, and every row has the same columns.
Your technique of repeating the groups of stock columns is a violation of First Normal Form, and it also has a lot of practical problems, for instance:
How do you know how many extra columns to create?
How do you search for a given value when you don't know which column it's in?
How do you enforce uniqueness?
The simplest solution is as #OGHaza described, store extra stock/quantity data on rows in another table. That way the problems above are solved.
You don't need to create extra columns, just extra rows, which is easy with INSERT.
You can search for a given value over one column to find it.
You can put constraints on the column.
If you really want to understand relational concepts, a nice book that is easy to read is: SQL and Relational Theory: How to Write Accurate SQL Code by C. J. Date.
There are also situations where you want to expand a table definition with dynamic columns that aren't repeating -- they're just new attributes. This is not relational, but it doesn't mean that we don't need some data modeling techniques to handle the scenario you describe.
For this type of problem, you might like to read my presentation Extensible Data Modeling with MySQL, for an overview of different solutions, and their pros and cons.
PS: Fourth and Fifth normal form have nothing to do with this scenario. Your lecturer obviously doesn't understand them.

E-Commerce Database Design for Orders Table

I am designing a schema for E-Commerce app, in which I would have 3 tables i.e Orders, Products, Customers.
So Should we store customer_id and product_id in Orders table straightaway.
The limitation to this is when a product or customer updates their attributes( i.e product price or customer name ), the orders table doesn't reflect them.
For Ex: A Customer bought a product at $10, but later on the product price gets updated to $20.So now when we are referring to this order by product id we would get the result as it was bought at $20 instead of $10.
SOLUTION 1:
One solution would be to insert a new row into products table whenever an updates occur and perform a soft delete to that product so that it can be referenced from orders table.
SOLUTION 2:
Store most of the details in product and customer details in orders table.
SOLUTION 3:
Create a temporary table of customers and products whenever there is an update to these tables.
I am very much open to any other suggestions.
One thing that you seem to be missing is a orderLineItem table for anything other than the most simple solution, where there is one product/order.
Now, that being said, you can do the products table in several ways.
Assuming that price is your only variable in the products table that you want to change, you can have a separate pricePoints table, that would store the price for any item at any given time. You would then use the ID from this table in your orders table and use that to get to the productID from the products table. A slightly more inefficient way to store this (but faster for retrieval) would be to store both the productId and the pricePointId in the orders table.
You could also do this by simply storing the price paid amount in the orders table. This gives you a little more flexibility to add discounts and pricing rules. You do need to be concerned about auditing the price though if you do it this way. Why was this price charged for this line at this time is going to be a common question.
You need to know how much the customer paid for the product at any time. It's not so important to know how much the customer would have paid for the order if they bought it today.
Customers are a slightly different issue. Some of the information in a customer table is transient. Some of it has to be fixed for the order. Lets say that the customer has a name, address, billing address and shipping address. At the time of the order, the shipping and billing addresses have to be absolutely fixed. You don't want to go back in three weeks and discover that the shipping address was changed. But, by the same token, you might like the name to be updated if a customer changes their maiden name, for example.
Now, all that being said, we aren't going to design your schema for you. There are a lot of good resources out there for how to design a simple e-commerce database.

How to expand this tables to support revision history

I have 3 tables:
pricelists (pricelist_id, name)
prices (price_id, pricelist_id, price, note)
tickets (ticket_id, price_id, name, time)
So, the main reason for versioning prices is because prices can be changed in future and I want to keep information about past prices for statistics, and I want to tickets has real price, not future changed price.
Can you please give me some example of queries?
I suppose one possible approach is making two price tables instead of two: the first one will store some generic price-related data (like 'note' and 'pricelist_id' link, as these won't change with time), and the second one will store the actual prices (along with, probably, timestamps of their activation - but that's not necessary):
prices (price_id, pricelist_id, note)
price_versions (price_ver_id, price_id, price, started_at, ended_at)
tickets (ticket_id, price_ver_id, name, issued_at)
As you see, you refer to price_versions in your tickets to get the specific price. But you can easily collect the generic price-related information as well (by joining the prices table from there).
This approach lets you construct an additional constraint, checking that issued_at is not before started_at and not after ended_at (it that NOT NULL in the corresponding row). But that's an addition, not a requirement, I suppose.