I’m creating a database design for a webshop. I want to store products with different attributes. Currently I have one table with +100 columns, but I want to optimize this.
This is what I’ve come up with so far. I have some questions (see below) about my design so far.
Disclaimer: this is a database DESIGN. I do not have some php/sql-code because I don’t’ know if this is the correct way to do it. I will try to make this question as substantiated as possible.
Here we go…
I have 3 tables:
The first table is the table “products” which will store all the general information about each product (id, name, sku, images, …)
The second table is the table “attributes” which will store all the attributes (eg. color, width, height, has_bluetooth, …) but NOT the values
The third table stores the values for each attribute (table "attributes_values")
Table: products
Product_id | Name | SKU
------------------------------------------------------
1 | iPhone 7 | iphone7
2 | HTC One | htcone
3 | Galaxy S8 | galaxys8
As you can see, I have 3 products in my database
Table: attributes
Attribute_id | Name
---------------------------------------
1 | Color
2 | Weight
3 | Height
As you can see, I have 3 different attributes in my database – note that some products will not have each attribute
Table: attributes_values
Attribute_value_id | Attribute_id | Product_id | Value
-----------------------------------------------------------------------
1 | 1 | 1 | Black
2 | 2 | 1 | 0,125 kg
3 | 3 | 1 | 10 cm
4 | 1 | 2 | Gold
5 | 1 | 2 | 0,15 kg
As you can see, product 1 (the iphone) has 3 attributes, product 2 (the htc one) has 2 attributes and product 3 (the galaxy s8) has zero attributes.
My questions
First of all, is this a good approach? I want to create a “dashboard” in PHP where I can dynamically add new attributes when I add new types of products to my database. That’s why I separated the attributes name and value in 2 different tables.
Secondly, how do I fetch the information from the database. I want to select the product + all the attributes it has (and the values associated with each attribute). I think this is the way to do it. Please correct me if I’m wrong.
SELECT
p.name, // the product name
p.sku, // the product SKU
v.value, // the attribute value
a.name // the attribute name
FROM
products AS p
LEFT JOIN
attributes_values AS v
ON
p.product_id = v.product_id
LEFT JOIN
attributes AS a
ON
v.attribute_id = a.attribute_id
I hope my questions are as clear as possible. If not, feel free to ask. English is not my native language so excuse me for some grammar errors. Thank you all!
I have found the following links, maybe they can help.
https://dba.stackexchange.com/questions/24636/product-attribute-list-design-pattern
How to design a product table for many kinds of product where each product has many parameters
http://www.practicalecommerce.com/A-Better-Way-to-Store-Ecommerce-Product-Information
http://buysql.com/mysql/14-how-to-automate-pivot-tables.html
Related
I am experiencing difficulty in MySQL database design.
I have the following tables:
school_table
id | school_name
---------------------------
1 | success primary school
stage_table
id | stage_name
---------------------------
1 | nursery
2 | primary
3 | secondary
school_stage_table
id | school_id | stage_id
---------------------------
1 | 1 | 1
2 | 1 | 2
school_stage_table is a linking table. This is because there is many to many relationship between school and stage, that is a single school may have many stages, and the same stage may have many schools.
The problem comes here:
Each stage has different attributes, and therefore different attribute values for different schools.
How do I model this scenario in a database? Need your help.
As you previously said that you are having some issues that how to store different attributes of each stage of each schools.
Here you can take one table which will store all the attributes of each stage. You can use following table for storing attributes.
Table :
school_stage_attributes_table
id | school_stage_id | attributes_name | attributes_value
------------------------------------------------------------
1 | 1 | attrib_1 | value_1
2 | 1 | attrib_2 | value_2
One option here would be to create a stage_attribute table containing at least the following four columns:
stage_attribute
id | school_id | stage_id | attribute
Each record in this table would correspond to a single attribute for a single stage, e.g.
1 | 1 | 1 | 'nap time'
2 | 1 | 1 | 'breakfast'
3 | 1 | 3 | 'phys ed'
I suspect that some of the difficulty in your mind was with the possibility of adding attribute columns to the stage_table for each attribute. Of course, this is problematical because each stage could have different numbers or types of attributes, and it won't scale for many attributes. The option I gave above eliminates many of these problems by using an arbitrary number of records to store the stage attribute information.
You should use table school_stage_table for this different attribute values for different schools.
If You will use the same attributes schema for multiple schools, then there should be one more table called for example school_stage_options with fields
school_stage_options_id | stage_id | option1 | option2 ....
and later use school_stage_options_id in school_stage_table instead of using stage_id.
I wanted to ask you which could be the best approach creating my MySQL database structure having the following case.
I've got a table with items, which is not needed to describe as the only important field here is the ID.
Now, I'd like to be able to assign some attributes to each item - by its ID, of course. But I don't know exactly how to do it, as I'd like to keep it dynamic (so, I do not have to modify the table structure if I want to add a new attribute type).
What I think
I think - and, in fact, is the structure that I have right now - that I can make a table items_attributes with the following structure:
+----+---------+----------------+-----------------+
| id | item_id | attribute_name | attribute_value |
+----+---------+----------------+-----------------+
| 1 | 1 | place | Barcelona |
| 2 | 2 | author_name | Matt |
| 3 | 1 | author_name | Kate |
| 4 | 1 | pages | 200 |
| 5 | 1 | author_name | John |
+----+---------+----------------+-----------------+
I put data as an example for you to see that those attributes can be repeated (it's not a relation 1 to 1).
The problem with this approach
I have the need to make some querys, some of them for statistic purpouses, and if I have a lot of attributes for a lot of items, this can be a bit slow.
Furthermore - maybe because I'm not an expert on MySQL - everytime I want to make a search and find "those items that have 'place' = 'Barcelona' AND 'author_name' = 'John'", I end up having to make multiple JOINs for every condition.
Repeating the example before, my query would end up like:
SELECT *
FROM items its
JOIN items_attributes attr
ON its.id = attr.item_id
AND attr.attribute_name = 'place'
AND attr.attribute_value = 'Barcelona'
AND attr.attribute_name = 'author_name'
AND attr.attribute_value = 'John';
As you can see, this will return nothing, as an attribute_name cannot have two values at once in the same row, and an OR condition would not be what I'm searching for as the items MUST have both attributes values as stated.
So the only possibility is to make a JOIN on the same repeated table for every condition to search, which I think it's very slow to perform when there are a lot of terms to search for.
What I'd like
As I said, I'd like to be able to keep the attributes types dynamical, so by adding a new input on 'attribute_name' would be enough, without having to add a new column to a table. Also, as they are 1-N relationship, they cannot be put in the 'items' table as new columns.
If the structure, in your opinion, is the only one that can acheive my interests, if you could light up some ideas so the search queries are not a ton of JOINs it would be great, too.
I don't know if it's quite hard to get it as I've been struggling my head until now and I haven't come up with a solution. Hope you guys can help me with that!
In any case, thank you for your time and attention!
Kind regards.
You're thinking in the right direction, the direction of normalization. The normal for you would like to have in your database is the fifth normal form (or sixth, even). Stackoverflow on this matter.
Table Attribute:
+----+----------------+
| id | attribute_name |
+----+----------------+
| 1 | place |
| 2 | author name |
| 3 | pages |
+----+----------------+
Table ItemAttribute
+--------+----------------+
| item_id| attribute_id |
+--------+----------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
+--------+----------------+
So for each property of an object (item in this case) you create a new table and name it accordingly. It requires lots of joins, but your database will be highly flexible and organized. Good luck!
In my Opinion it should be something like this, i know there are a lot of table, but actually it normilizes your DB
Maybe that is why because i cant understant where you get your att_value column, and what should contains this columns
I need to store similar products with different attributes (like length or color), should I add this products with different ids or as one product with many attributes?
Main problem that I need possibility to see quantities of products by attribute (4 red boots, 3 blue etc) and need to implement attribute selector on product page.
If create one id how to store attributes in cart table with selected attributes and how to manage quantities?
If create many ids it is easy to manage quantities but how to implement attributes selection?
I think about some SKU for linking product id with attributes and attribute values. But how to link all together?
If you're building an ecommerce site, this domain concept is generally known as "variant"; see here.
Typically, you have a "products" table containing the major attributes of the item, and a "variants" table, linked back to the product by product ID.
If you're only ever dealing with similar products, you can have the variant table contain columns for all attributes (e.g. size, price, colour); if you have lots of different types of variant (e.g. clothes, shoes, sports equipment), you are at the "inheritance modeling using relational databases"; see this answer.
Tables are there to store any data which you have. If you need any particular information about those entries you have to use different queries. For example assume the following product table:
+----+-------------+-------------+
| ID | ProductNAme | ProductType |
+----+-------------+-------------+
| 1 | Product A | Type A |
+----+-------------+-------------+
| 2 | Product B | Type A |
+----+-------------+-------------+
| 3 | Product C | Type B |
+----+-------------+-------------+
You can get the quantity of Type A Products by writing the following query (view This Link for a better understanding):
Select count(*) as CountOfProduct from Product where ProductType="Type A";
EDIT
You mentioned in your comment that one product could possibly have different types and different quantities. You can do that in one table but that looks messy. If you want this to happen you need two build a 1-Many **relationship** between two tables called product and type.
The type table could be something like this :
| TYPE_ID | TYPE |
|---------|--------|
| 1 | Type A |
| 2 | Type B |
| 3 | Type C |
and your product table could be something like this:
| PRODUCT_ID | PRODUCTNAME | QUANTITY | TYPE_ID |
|------------|-------------|----------|---------|
| 1 | Product A | 3 | 1 |
| 2 | Product B | 2 | 1 |
| 3 | Product C | 1 | 2 |
| 4 | Product C | 5 | 3 |
**take note that type_id is the foreign key which builds the relation between these two tables. And since you can have multiple products with the same type (like product C in this example) this table will be your many table and type table will be your one table. Hence, with putting the foreign key in the many table you will establish the one to many relationship.
Now, in order to combine (or in other words to join) these two tables you will need to write a join query as following:
select ProductName,quantity,Type from Product p
inner join type t on t.type_id=p.type_id
and the result will be what you want:
| PRODUCTNAME | QUANTITY | TYPE |
|-------------|----------|--------|
| Product A | 3 | Type A |
| Product B | 2 | Type A |
| Product C | 1 | Type B |
| Product C | 5 | Type C |
Check this link out for the fiddle
Currently I'm dealing with kinda large mySQL transactional database for one e-commerce project. We obtain data from e-shops including products sold. Each e-shop adds information about similarities between products and list them as groups. So, for instance shop A sends information:
Group 1: iPhone blue, iPhone black, iPhone green
Group 2: iPad blue, iPad black, iPad green, etc.
Another e-shop sends this kind of information:
Group 3: iPhone pink, iPhone black
Group 4: iPad blue, iPad pink
Each product is stored in table Products: (Important: This table has about 150 000 000 rows)
Id | Name
------------------
1 | iPhone blue
2 | iPhone black
3 | iPhone green
4 | iPhone pink
5 | iPad blue
6 | iPad black
7 | iPad green
8 | iPad pink
Also, there is a table Groups with groups stated above: (M:N relationship)
Id | Id_product | Group
--------------------------
1 | 1 | 1
2 | 2 | 1
3 | 3 | 1
4 | 5 | 2
5 | 6 | 2
6 | 7 | 2
7 | 4 | 3
8 | 1 | 3
9 | 5 | 4
10 | 8 | 4
Now, the problem is that groups 1 + 3 and groups 2 + 4 should be merged together.
Current (horrible) solution to this problem is based on obtaing all groups for the product (by GROUP_CONCAT function in query) and then all products from these groups. Then updating table groups to merge these groups into one.
Main problems with this approach are:
Very problematic computational complexity.
Groups obtained from e-shops can be wrong(!). Imagine this group:
Group5: iPhone Black, iPad Black. Taking this group into account, whole separation process is wrong. You end up with one group with iPhones and iPads together (that's wrong).
So, now, finally, the question:
Any ideas how to approach this problem? Just hints/tips will be enough, I'm just totally stuck with lack of my knowledge.
I was playing around with fuzzy-hashing algorithms / k-means clustering, but it seems to me that it is not suitable for this problem. Fuzzy-hashing seems to be getting into account names of the products (that can be good with iPhone, but cannot image it with T-shirts, their names are not very "well-prepared", so it's hard to guess differences just from the name). Am I missing something?
So, any idea?
Anyway, just for the purpose of solving this particular problem, it's possible to introduce different database solution, there's no problem in that.
Thanks in advance:)
Chmelda
An idea might be to add a table "group_conversion" which translates each external group number into your own group number.
In this case the table would look like:
Group_external | NameMatch | ID_my_group
----------------------------------------
1 | null | 1
2 | null | 2
3 | null | 1
4 | null | 2
5 | "IPhone%" | 1
5 | "IPad%" | 2
When inserting new data coming from an e-shop, you should first translate the incoming group number to your own group numbering, before adding it to the Groups table.
The NameMatch field is only used if you want to separate products whitin an incoming group (the Group5 you mentioned).
So if this field is null, just convert the ID. Otherwise only convert the ID if the name of the product matches NameMatch.
To convert your current data it might help to create a new table (e.g. Groups2) which has the same fields as Groups, with the only difference that Group is a reference to the new group numbering.
You can then fill the new table by converting each record of Groups.
After conversion is done, drop the Groups table and rename the Groups2 table.
In this way you will get a much smaller table size for Groups and the table already contains merged data, so no separate queries are needed for merging.
Hope this will help!
MORE DETAILS:
Both of you recomend using JOIN. But the main problem is how to assign multiple SUBJECTS PER EACH CLASS without using multiple duplicate values. I will have ~200 de classes, with ~30 subjects per class. That means if 2 classes share the same 20 subjects, i will have 40 rows, all with "class_id = 1" but with "subjects_Id =1, subjects_id=2, etc" Its not very ergonomic. Any other ideas? Thanks for your time!
So, I am here again asking for your time and help friends.
I have a database that its almost ok. But I am stuck at trying how to link multiple values from a table to on collumn on another.
Let me be more explicit.
I have this table:
CLASSES
id | class_name | Matters |
-----------------------------
1 | Class1 | 13.4.2013 |
2 | Class2 | 14.4.2013 |
And this table:
Subjects
mat_id | show title |
-----------------
1 | English |
2 | French |
Now the problem is this. Each CLASS (e.g. CLASS1) should be able to study more Subjects at once. For example, CLASS 1 should be linked with subject (mat_id) 1, 3, 5, 6.
How to do this without repeating myself, and optimize the database? I tought that I should do it like so, but its not convenient :
CREATE A NEW TABLE named
SUBJECTS_PER_CLASS
id | class_id | mat_id |
----------------------------
1 | 1 | 1 |
2 | 1 | 3 |
BUT then I dont know how to query it. Any ideas? Any help will be greatly appreciated!
THANKS!
SELECT
*
FROM
CLASSES
JOIN
SUBJECTS_PER_CLASS
ON
CLASSES.ID = SUBJECTS_PER_CLASS.class_id
JOIN
Subjects
ON
Subjects.id = SUBJECTS_PER_CLASS.mat_id
You can use join command.
Reference 1
Reference 2