First of all, I apologize for the bad English you are about to read...
I'm trying to develop a little e-commerce web application (from scratch - without using platforms like Magento, OpenCart, Shopify...) for a pizza delivery in the city where I live. The restaurant also sells some italian food, like pasta, fish and meat.
I'm stuck in a relational database problem, I will explain what I did in the database. I will write the tables structures followed by some examples records.
Unlike the pastas, the pizza's price varies according to size (an attribute).
The data will be displayed in the following way (please see the picture below):
Showing a pizza example record in the front-end. When the user selects the size, the price will be displayed below and two controls to add or substract the quantity of that product (with that size) will also be displayed.
This is the case of one pizza with one attribute (an attribute that affects the price), because there are some attributes that not affects the price, i.e: the cooking or doneness. Another case is that a product that have more than one attribute that affect the price.
In summary:
Product without attributes, it has an only one price.
Product with only one attribute that affect the price.
Product with two or more attributes that affect the price.
MySQL Tables:
Categories(ID, name):
1, Pizzas
2, Pastas
_
Products(ID, category_id, name, description)
1, 1, Margherita, Lorem ipsum
2, 1, 4 Stagioni, Lorem ipsum
3, 1, Capricciosa, Lorem ipsum
4, 2, Bologna, Lorem ipsum
5, 2, Pesto, Lorem ipsum
_
Attributes (ID, name)
1, Size
2, Cooking
_
Meta_attributes(ID, attribute_id, name)
1, 1, Small
2, 1, Medium
3, 1, Big
4, 2, Blue
5, 2, Medium well
6, 2, Well done
7, 2, Overcooked
_
meta_attributes_values(ID, product_id, meta_attrib_id, value)
1, 1, 1, 12
2, 1, 2, 16
3, 1, 3, 19
4, 2, 1, 14
5, 2, 2, 18
6, 2, 3, 20
_
In this schema, a product can have a value if and only if it has a meta_atrib and in order to have an meta_atrib it must have an attrib. But the pasta is "linear" it no have any attrib, for one pasta product there are only one price.
Questions:
How should be the database to handle all these cases?
What about special cases where one attribute influences another? For example, suppose that the price of a pizza varies according to its size (this is true), but suppose that it also has an attribute called "extra" and that the price of the extra attribute varies according to the size of the pizza, Because being larger will require more. I know that the example is not very clear but I hope I have made the case clear and express myself well.
Thanks for reading!
It's important to note that the schema you've described doesn't represent an actual order, it represents the abstract concept of a pizza. A graph of Products, Attributes, Meta-Attributes, and Values is far more complicated than an itemized order needs to be.
What's really in an order? There are Products, each of which has a base price; and there are, as you note, things which affect the price of a Product. These modifiers come in at least two types:
Additive modifiers tack on a flat sum to the base price. A "medium" pizza costs $4 above the base; a "large" costs $7 more.
Dependent modifiers change the base price after it's adjusted by additive modifiers. The simplest form of dependent modifier is a multiplier: whatever the adjusted price of a pizza is, one with "extra hot peppers" costs 0.10x more than that.
With any luck, that's all you have to deal with. If "extra peppers" instead costs $0.50 on a small, $0.60 on a medium, and $1.00 on a large, you have to track all three and correlate with the size modifier since the addition isn't a consistent function of adjusted price. Treating additive modifiers like size independently -- for example, by having base, medium, and large prices in Product -- may be more effective in that case.
It would be possible to achieve a simpler representation still by treating products and attributes identically and storing them in a single table with a foreign key to itself to represent the parent-child relationship. Effectively, you'd have no Products, only Attributes. And "Margherita" would be an Attribute that adds $12 to an item base price of $0.
But getting back to the concrete, if you need to track Orders with Order_Items too, even a one-row-per-attribute solution is unwieldy since you have a profusion of foreign keys in each line item of the order. In this case, it may be best to store your sub-items (or everything, if you roll it all into one table) in a JSON field, such that your Order_Items table looks like this:
id order_id subtotal attributes
1 1 17.60 [{"name": "Margherita", "adds": 12.00}, {"name": "Medium", "adds": 4}, {"name": "Extra hot peppers", "multiplier": 0.10}]
2 1 12.00 [{"name": "Pesto", "adds": 12.00}]
This is a) denormalized and b) breaks referential integrity. Both of these, in this instance, are good things! If you ever adjust prices or even take something off the menu, you don't want to screw up your bookkeeping or trip a foreign key constraint error.
Related
From the nested sets reference document written by Mike Hyller and other blogs, I could understand how hierarchies are being managed in RDBMS. I was also able to successfully implement the model for one of my projects. I am currently working on a problem which also has hierarchy, but the nodes are built from the bottom. I am using MySQL.
Consider I have 10 objects, I initially create rows for them in a table. Then, there is a table which has the left and right values that are required for implementing the nested sets model. So in this table, I group these 10 objects into two sets, say two bags, 5 objects in one bag and other 5 objects in one bag (based on some logic). Now these two bags are grouped together to form a bigger bag. Likewise, such bags are grouped together to form a big container.
I hope the example is clear to you to get an idea of what I am trying to achieve here. This is the opposite of applying the traditional nested set model where I build the sets from the top.
Can you please suggest me whether nested sets can be applied here? If yes, will changing the update query during insertion be sufficient to form the entire hierarchy? If you don't suggest, what other techniques can be used to tackle such problems?
Nested sets model works for any hierarchy, as long as it's non-overlapping (i.e. one child can have at most one parent).
Your model seems to have a predefined hierarchy ("objects", "bags" and "containers" being different entities with different properties). If it's the case indeed, you don't need nested sets at all, a simple set of foreign key constraints will suffice.
If it's not though (say, if a "bag" can be promoted to a "container", or there can be "containers" containing other "containers" etc.), you will need to have some kind of a hierarchy model indeed, and nested sets can serve as one as well.
One way to implement one would be to add references to you "bags" or "containers" or whatever to the table which holds your left and right values for your "objects":
CREATE TABLE nested_sets
(
ref BIGINT NOT NULL,
type INT NOT NULL -- 1 = object, 2 = set, 3 = bag
left BIGINT,
right BIGINT
)
INSERT
INTO nested_sets
VALUES (1, 1, 1, 1),
(2, 1, 2, 2),
(3, 1, 3, 3), -- 3 objects in bag 1
(4, 1, 4, 4),
(5, 1, 5, 5),
(6, 1, 6, 6), -- 3 objects in bag 2
(1, 2, 1, 3), -- bag 1, containing objects 1 to 3
(2, 2, 4, 6), -- bag 2, containing objects 4 to 6
(1, 3, 1, 6), -- container 1, containing bags 1 and 2 and, by extension, objects 1 to 6
You may also want to move left and right fields from the nested_sets table to the main tables describing the entities, or, alternatively, you may want to move all entities into a single table. This depends on how rigid your definitions of "bag", "container" and "object" are.
Dining room specializes on complex dinners. Have collection of recipes (each of them collect rates of the products). Every product have changeable price.
Is it the best design?
Recipe(r_id, r_title, r_category, r_price)
Product(p_id, p_title, p_price)
UsingProducts(r_id, p_id, amount)
I am just not sure about UsingProducts..
The design looks quite okay.
As zerkms mentioned, you're lacking units. That doesn't have to be a problem, as your product can be "100 g flour" so the unit is implicit. However, when printing the recipe, you would print "5 x 100 g flour" instead of "500 g flour". It would also print "10 x 100 g flour" instead of "1 kilo flour".
Just think about whether this an issue for you and if you even need unit conversion like 1000 g = 1 kilo.
Another point is your category. So a recipe can only belong to one category. So you won't have something like "vegetarian" and "soups" with the problem where to place a vegetarian soup, but use distinct categories instead. Okay. However, don't you want a table for them, so to be able to easily select them? If you want to stay with this design you should at least make them an enum column (something special in MySQL), so you dont mistakenly have recipes in "soups" and others in "suops".
At last: What is the r_price for? Shouldn't that be the the sum of all sub prices (product price x amount)? Don't hold data redundantly. This must not be done. Otherwise inconsistencies can occur (e.g. 10$ + 10$ = 30$). Remove r_price from table recipe to have a normalized database.
I have a database in mysql which has a collection of attributes (ex. 'weight', 'height', 'no of pages' etc) and attribute values (ex. '30 tons', '12 inches', '2 pgs' etc) and mapped with the respective product ids.
The data has been collected from different sites and hence the attribute values have different formats (ex. '222 pgs' or '222 pages' or '222') (ex2. '12 inches', '12 meters', '12 cms').
What I need to do is that I have to compare the values of same attributes of different products. So I have to compare '222 pgs' with '222 pages' for all the attributes which differ in formats.
There are around 4000 attributes and the number will increase further. Is there any way to compare these without having to assign each attribute a specific type individually? Or what is the fastest way to compare these?
Well, until they invent a clairvoyant computer, a human being will have to tell it that pgs and pages mean the same thing and that inches and meters are convertible.
You'll have to sanitize the data one way or another. I'd probably start by identifying units that measure the same dimension1 and common aliases2 for each unit, then parse the data to split the quantity from the unit and normalize3 the unit. Once you have done that, the data becomes directly comparable.
But all this is really just a remedy for the problem that should not have been there in the first place, were the database designed properly.
1 A "mass" is a dimension measured by units such as kg, t, lb etc. A "length" is a dimension measured by m, km, in etc.
2 E.g. an in and inch denote exactly the same unit, pgs and pages are the same etc.
3 I.e. make sure a particular dimension is always represented by the same unit: for example convert all lengths to m, all masses to kg, all pages to pages etc.
You haven't explained what you want to do after you find out that attributes for a pair of products differ (while still meaning the same thing).
I.e.: if I see that in Instance A has field Length set to "12 pgs" and Instance B has Length reporting "12 pages" what do you do?
List this? Autocorrect? Drop one of the two values? Open a window for a human user to correct?
Personally I'd go for a "select attribute,count(*) from X group by attribute" so that you can find out the most common spelling of the unit, and then you can also write corrective scripts that may automatically convert ".. pgs" to " pages" as soon as you have decided the correct representation.
Of course this will not help at all unless you enforce correct spelling of the units, and this requires for sure better input-output filters, including the main UI, but also any kind of bulk uploader utility you may use to create or update products.
A redesign of the DB to add "Unit" as an extra, categorized attribute for each measure would also help a lot.
I am developing product database, for sizes i created a separate table PRODUCT_SIZE(id,sizetext)
e.g. (1,'Small') ,(2,'Large'), (3,'Extra Large'),...
I provided These sizes list as checkbox, when a product is added, all possible sizes can be selected against current product.
e.g. for T-Shirt, SMALL, and LARGE sizes selected.
these 2 Sized are available against each new stock purchased entry.
Now i came to know, that there can be different size units, some items can be in inches, some in kg, and some in meters.
I have a altered solution in mind:
to alter table
PRODUCT_SIZE(id,sizetext, UNitType);
Now it can be: (1,'5','KG') ,(2,'10','KG'), (3,'2.5'.'Inches'),...
Is ther any better approch, suggestion?
It seems like you're forcing 'clothing size', 'weight' and 'length' into one 'size' attribute.
Try these tables:
product (product_id, name)
"Nike t-shirt"
attribute_group (attribute_group_id, name)
"Shirt size", "Weight", "Length", etc.
attribute_value (attribute_value_id, attribute_group_id, name)
"Shirt size" would have rows for "Small", "Large", etc.
product_attribute (product_id, attribute_value)
"Nike t-shirt" is "Large"
Add a "display order" to attribute_value, too (so "Small" can be displayed before "Large").
Do this for your other attributes, too.
I've done this for a production site, and I think it worked well.
Good luck.
Instead of making a seperate table for this, why don't you just put all of your dropdown options in an application scoped variable? Then you can just add that data right into a field in product as a string and deal with the different options/units programmatically.
I made a database storing clothes where the sizes were a few types.One article has sizes like xs s m l other is
26 27 28 29 30 and so on.
I decided to do this:
# on one side in the script i define size types and names;
$sizeTypes[1] = [XS, S, M, L];
$sizeTypes[2] = [29, 30, 31, 32];
#The and so on
#and on the other side in the database, there are just two columns
size_type_id(int) | size_qty |
# so if I have one article with 3 pieces of size S 2 pieces of size M and 5 pieces of size L the database will store:
size_type_id| size_qty |
1 |0:0;1:3;2:2;3:5|
then in the script I just translate it so that 0 of type 1 is XS 1 of type 1 is S 2 of type 2 is 31 and so on
How should I store a user's height and weight in a MySQL database such that I can use the information to find users within a certain height or weight? Also, I will need to be able to display this information in either English or metric system.
My idea is to store the information for height in centimeters and weight in kilograms (I prefer metric over English). I can even let the user enter their information and English system, but do the conversion to metric before saving. I think converting kilograms to pounds might be easy to do in SQL, but I'm not sure how easy it would be to convert 178 centimeters to 5'10" (rounded slightly down).
Should I be saving English and metric values in the database so that I don't need to do conversions when I do my queries? Sounds like a bad idea to store derived/computed values.
There are several ways... one is to just have two numeric columns, one for height, one for weight, then do the conversions (if necessary) at display time. Another is to create a "height" table and a "weight" table, each with a primary key that is linked from another table. Then you can store both English and metric values in these tables (along with any other meta info you want):
CREATE TABLE height (
id SERIAL PRIMARY KEY,
english VARCHAR,
inches INT,
cm INT,
hands INT // As in, the height of a horse
);
INSERT INTO height VALUES
(1,'4 feet', 48, 122, 12),
(2,'4 feet, 1 inch', 49, 124, 12),
(3,'4 feet, 2 inches', 50, 127, 12),
(3,'4 feet, 3 inches', 51, 130, 12),
....
You get the idea...
Then your users table will reference the height and weight tables--and possibly many other dimension tables--astrological sign, marital status, etc.
CREATE TABLE users (
uid SERIAL PRIMARY KEY,
height INT REFERENCES height(id),
weight INT references weight(id),
sign INT references sign(id),
...
);
Then to do a search for users between 4 and 5 feet:
SELECT *
FROM users
JOIN height ON users.height = height.id
WHERE height.inches >= 48 AND height.inches <= 60;
Several advantages to this method:
You don't have to duplicate the "effort" (as if it were any real work) to do the conversion on display--just select the format you wish to display!
It makes populating drop-down boxes in an HTML select super easy--just SELECT english FROM height ORDER BY inches, for instance.
It makes your logic for various dimensions--including non-numerical ones (like astrological signs) obviously similar--you don't have special case code all over the place for each data type.
It scales really well
It makes it easy to add new representations of your data (for instance, to add the 'hands' column to the height table)
I would do it the way that you have said you would like to do it, but on the converting part, you would not convert 178 centimeters to 5'10", you would convert it to 70", then if need be, convert that into 5'10".
Think of 5'10" as either 70" or 5.8333333'. In that case, converting betwen 70" or 5.83333 is just a multiplication, so its easy to store in the db as centimeters if you so choose.
The issue of what the user sees is a presentation issue and nothing to do with the database.
I agree that storing computed values in this case is not ok. Your choices are perfect.
However, I would do the computations at the application level and query the DB with those values - depending on the language your application is written in , I am sure there are plenty o libraries/modules that are made that can compute those transformations.
Edit - to address the issue of storing computed values in DB:
While this is considered to be a bad practice in working with DBs, I usually am not 100% against this practice - just 90%.
I tend to store computed values in DB only when the computations are complex and would take enormous resources to get to the result wanted - this is clearly not the case.
If you would store computed values here you would have only the disadvantages of this technique - when modifying a record, you would have to modify the data in multiple places to keep the consistency of your DB