views:

112

answers:

2

I have a shopping cart in which I have to keep track of stock, and this includes stock with attributes. For example:

shirt
 -- blue, small  = 50
 -- blue, medium = 22
 -- blue, large  = 53
 -- red,  small  = 15
 etc...

These fields are currently separated by commas in the database and the IDs would be as follows:

1,3
1,4
1,5
2,3

where each number represents a specific attribute (1=blue,3=small). The problem is that this then becomes very hard to work with as the data is not atomic in the database, but I cannot think how to structure the table, as there could be infinitely many combinations of items such as:

blue, small, long-sleeved

Can anybody suggest a better way of storing this information in the database? I thought of using lookup tables but this would not work due to the varying number of attributes.

+4  A: 

Table: Shirt { shirt_id }

Table: ShirtAttributeDefinitions{ attribute_id , attribute_description }

Table: ShirtAttribute{ shirt_id , attribute_id }

this way, you can keep defining new ShirtAttributes by adding records to Table:ShirtAttributeDefinitions.

Example:

you have two shirts: { shirt_id:1 }, { shirt_id:2 }

you have five shirt attributes: {attribute_id:1, attribute_description:blue }, {attribute_id:2, attribute_description:small }, {attribute_id:3, attribute_description:ladies }, {attribute_id:4, attribute_description:red }, {attribute_id:5, attribute_description:men }

Now your first shirt is a blue small ladies shirt, and the second one is a red mens shirt. So your ShirtAttribute table will have these recrods:

{shirt_id:1, attribute_id:1}, {shirt_id:1, attribute_id:2}, {shirt_id:1, attribute_id:3}, {shirt_id:2, attribute_id:4}, {shirt_id:2, attribute_id:5}

Although this approach works, its best to make these attributes fields of the Shirt table. This way you can make sure that a shirt does not have both 'men' and 'ladies' attribute. This method is just a dirty approach that strictly conforms to your question.

jrh.

Here Be Wolves
+1. This is in fact the "cleanest" way to do this in a RDBMS -- new attributes can be added later without schema changes. But as you say, you unfortunately do lose some opportunities to enforce constraints.
j_random_hacker
indeed. A common application like a shopping cart really doesn't need this generatlity... or so I feel.
Here Be Wolves
When you say "shirt" table are you referring to the products table as shirt was mean as an example? There can potentially be many types of products.
xenon
yes, there ought can be many types of products, and a table for each type of product should exist.
Here Be Wolves
Ouch, that potentially could be a lot of tables then :S
xenon
@harshath.jr: A table-per-product approach performs well if there are few products and they don't change much, but surely that's not the case here? Table-per-product makes joins (needed for queries that combine results across product types, and for searches) very difficult.
j_random_hacker
@harshath.jr: If you actually meant that there should be a set of 3 tables for each product, then I misunderstood your original solution -- like xenon I assumed your Shirts table was actually for all products. I'm confused -- why limit it to shirts, when one set of 3 tables (Product, ProductAttributeDefinitions, ProductAttribute) works just fine, and enables simple cross-product queries?
j_random_hacker
@xenon: it would be a lot of tables, yes. But this will only add to the simplicity of your setup.
Here Be Wolves
@j_random_hacker: I limit the three tables to one product type becuase the attributes to other product types (say USB Sticks) may not apply to Shirts. And the problem of join can be solved rather easily: create a view that consolidates your data into meaningful rows. Query the view when you want to extract data from your databse.
Here Be Wolves
+1  A: 

what's wrong with an Attributes table?

create table Attributes (
    Id int not null identity etc,
    CartItemId int not null foreign-key etc,
    Name nvarchar(32),
    Value nvarchar(32)
)

?

Steven A. Lowe
Indeed, this would be the way I would recommend. Of course, this method does have an associated loss of generatlity.
Here Be Wolves
xenon
Steven A. Lowe
In the method you outline I would need one row for each combination i.e Small Blue 140cm, Small Red 140cm which would not help as you would have redundant data, such as small being in there. Then say I no longer wanted small but need the 140cm ones to remain, I would have to remove the rows and insert new rows for that combination
xenon
@[xenon]: ok, change CartItemId to AttributeGroupId, then add another table StockAttributeGroup to associate attribute-groups with stock items.
Steven A. Lowe
hmm, i suppose this is the best way to do it from a data point of you but from the front end this might prove difficult to use for most of the people likely to be using it :P Thanks for your solution :)
xenon
@[xenon]: you're welcome. Note that if your front-end likes comma-delimited lists better, you can use COALESCE (in MS SQL) to provide them
Steven A. Lowe