Reputation: 1943
I feel this is a very basic question, still I cant figure out a good way of doing it even though I've worked quite a bit with databases. Also can't seem to find anything when googling for it.
Let's say I have a bunch of tables, each describing a type of item.
For instance:
Table candles:
id█ name █length█diameter▌burntime▌
▀▀█▀▀▀▀▀▀▀▀█▀▀▀▀▀▀█▀▀▀▀▀▀▀▀█▀▀▀▀▀▀▀▀▌
1 █ candle1█54 █ 6 █ 180 ▌
69█ candle2█29 █ 16 █ 145 ▌
80█ candle3█100 █ 40 █ 110 ▌
Table coasters:
id█ name █thickness█diameter▌color ▌
▀▀█▀▀▀▀▀▀▀▀█▀▀▀▀▀▀▀▀▀█▀▀▀▀▀▀▀▀█▀▀▀▀▀▀▀▀▌
1 █coaster1█5 █ 20 █#ffffff ▌
6 █coaster2█8 █ 41 █#ff0051 ▌
8 █coaster3█7 █ 38 █#002199 ▌
Table baskets:
id█ name █size█
▀▀█▀▀▀▀▀▀▀▀█▀▀▀▀█
1 █basket1 █5 █
2 █basket2 █8 █
3 █basket3 █22 █
Now I have another table. Let's call it shoppingcarts. Each record in this table needs to hold an array of these items. This is where I don't know how to proceed. So I need a many to many relationship, which means I need a junction/pivot-table. But how?
Should I have one junction-table for each type of item? With a foreign key for cart-id and one for the record-id for candle/coaster/etc. Then when listing the content of the cart I need to check every single junction-table, one for each type of item. Doesn't sound very clean to me.
Or should I only have one single junction-table, which has a column for table-name and one for record-id? Seems very dirty too.
I suppose there's a much better way of doing this, but I can't think of it. How should this be done?
Upvotes: 0
Views: 532
Reputation: 91482
One potential way of tackling this problem is to think about the items you have as objects, then think about how you might model these relationally. If you treat all of your items as objects, with properties, that have values, then this is a common schema for storing this information.
This is often referred to as O/R Mapping
A simple relational example might look like:
Object - defines all of your items
+----+----------+---------+
| Id | Name | Type |
+----+----------+---------+
| 1 | Candle1 | Candle |
| 2 | Coaster1 | Coaster |
| 3 | Basket1 | Basket |
+----+----------+---------+
ObjectProperty - defines all of your properties
+----+-----------+----------+
| Id | Name | DataType |
+----+-----------+----------+
| 1 | Length | Float |
| 2 | Diameter | Float |
| 3 | Burntime | Float |
| 4 | Thickness | Float |
| 5 | Color | String |
| 6 | Size | Int |
+----+-----------+----------+
ObjectPropertyValue - defines the relationships between objects and properties, and the values
+----+----------+------------+----------+------------+-----------+-------------+
| Id | ObjectId | PropertyId | ValueInt | ValueFloat | ValueDate | ValueString |
+----+----------+------------+----------+------------+-----------+-------------+
| 1 | 1 | 1 | NULL | 54.0 | NULL | NULL |
| 2 | 1 | 2 | NULL | 29.0 | NULL | NULL |
| 3 | 3 | 6 | 5 | NULL | NULL | NULL |
+----+----------+------------+----------+------------+-----------+-------------+
To retrieve all of the values for a single object, you can run a query like:
SELECT OP.Name,
OP.DataType,
OPV.ValueInt,
OPV.ValueFloat,
OPV.ValueDate,
OPV.ValueString
FROM ObjectProperty OP
INNER JOIN ObjectPropertyValue OPV ON OP.Id = OPV.PropertyId
WHERE OPV.ObjectId = 1
There are several advantages to storing information in this format:
i.e. To search for all candles with a length less than 50:
SELECT O.Id,
O.Name,
OP.Name,
OPV.ValueFloat
FROM Object O
INNER JOIN ObjectPropertyValue OPV ON O.Id = OPV.ObjectId
INNER JOIN OpjectProperty OP ON OPV.PropertyId = OP.Id
WHERE OP.Name = 'Length'
AND O.Type = 'Candle'
AND OPV.ValueFloat <= 50.0;
However, there are also disadvantages:
There are other disadvantages too, but a good place to start reading about this subject in detail is O/R Mapping and Relational Data
Finally, an alternative solution for solving this problem is the concept of NoSQL, an alternative mechanism to storing objects relationally.
Jeff Atwood also has a great article on this very subject.
Upvotes: 1