4

I'm trying to create an inventory management schema where I can track the stock of various options related to products. A product may have any number of options, but for this example I'll use "size" and "color" options.

I've come up with three tables:

CREATE TABLE shop_options (
  option_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  option_name VARCHAR(40) NOT NULL,

  PRIMARY KEY (option_id)
);
INSERT INTO shop_options (option_id, option_name) VALUES (1, 'Size');
INSERT INTO shop_options (option_id, option_name) VALUES (2, 'Color');

CREATE TABLE shop_option_properties (
  prop_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  prop_name VARCHAR(40) NOT NULL,

  PRIMARY KEY (prop_id)
);
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (1, 'XS');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (2, 'S');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (3, 'M');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (4, 'L');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (5, 'XL');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (6, 'White');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (7, 'Black');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (8, 'Red');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (9, 'Green');
INSERT INTO shop_option_values (prop_id, prop_name) VALUES (10, 'Blue');

CREATE TABLE shop_product_options (
  product_id INTEGER UNSIGNED NOT NULL,
  option_id INTEGER UNSIGNED NOT NULL,
  prop_id INTEGER UNSIGNED DEFAULT NULL,
  surcharge DECIMAL(7,2) NOT NULL DEFAULT '0.00',
  stock INTEGER UNSIGNED DEFAULT NULL, /* NULL = stock is not limited */

  FOREIGN KEY (product_id)
    REFERENCES shop_products(product_id),
  FOREIGN KEY (option_id)
    REFERENCES shop_options(option_id),
  FOREIGN KEY (prop_id)
    REFERENCES shop_option_properties(prop_id)
);

I've determined that this won't work, because I may have "ten total small items" in stock, and "ten total white items" in stock, but not "ten total small white items" in stock.

How can I improve my schema to properly track stock for each option a product might have?

EDIT


I'm including the update below for anyone else having the same trouble with this as I was. I found the accepted answer difficult to understand at first. Basically, I can keep the schema I have above with the following modification on the shop_product_options table:

CREATE TABLE shop_product_options (
  po_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  product_id INTEGER UNSIGNED NOT NULL,
  option_id INTEGER UNSIGNED NOT NULL,
  prop_id INTEGER UNSIGNED NOT NULL,
  surcharge DECIMAL(7,2) UNSIGNED NOT NULL DEFAULT '0.00',
  stock INTEGER UNSIGNED DEFAULT NULL,

  PRIMARY KEY (po_id, product_id, option_id, prop_id),
  FOREIGN KEY (product_id)
    REFERENCES shop_products(product_id),
  FOREIGN KEY (option_id)
    REFERENCES shop_options(option_id),
  FOREIGN KEY (prop_id)
    REFERENCES shop_option_properties(prop_id)
);

With the added po_id and combination of keys as primary, I can now insert and extract "grouped" data as follows:

INSERT INTO shop_products (product_id, title) VALUES (1, 'Womens Shoe');

INSERT INTO shop_product_options (po_id, product_id, option_id, prop_id, surcharge, stock)
  VALUES (1, 1, 1, 3, '0.00', 10);
INSERT INTO shop_product_options (po_id, product_id, option_id, prop_id, surcharge, stock)
  VALUES (1, 1, 2, 9, '0.50', 20);
INSERT INTO shop_product_options (po_id, product_id, option_id, prop_id, surcharge, stock)
  VALUES (2, 1, 1, 5, '1.00', 30);
INSERT INTO shop_product_options (po_id, product_id, option_id, prop_id, surcharge, stock)
  VALUES (2, 1, 2, 9, '0.75', 40);

SELECT t1.po_id, t2.title, t3.option_name, t4.prop_name, t1.surcharge, t1.stock FROM shop_product_options AS t1
  JOIN shop_products AS t2 ON t1.product_id = t2.product_id
  JOIN shop_options AS t3 ON t1.option_id = t3.option_id
  JOIN shop_option_properties AS t4 ON t1.prop_id = t4.prop_id
WHERE t1.product_id = 1 ORDER BY t1.po_id ASC;

This results in a size M green womens shoe, and size XL green womens shoe, with different stock quantities on the sizes and colors for each.

mister martin
  • 6,197
  • 4
  • 30
  • 63
  • 1
    You can start from [here](http://www.databaseanswers.org/data_models/). – Mark Jan 09 '14 at 02:26
  • @ChristianMark Can you be more specific? I've looked through half the links on that page and have yet to find one that relates to my problem. – mister martin Jan 09 '14 at 14:17

2 Answers2

5

I think the draft model (following 6NF and 3NF) will help you.
I simplified naming convention by removing 'shop' keyword.
(Also shop entity may leads a separate concept AKA SaaS)

SqlFiddle Demo

enter image description here

About the questions in the comments:

Is it possible to have a unique product ID

Yes, it is a common pattern to use surrogate identifier on your tables. As you may see in the article, that will come with its pros and cons.

For example, in the question, you will see that primary key of ProductSpecification table is a composition of ProductTypeOptions, OptionValue and Product foreign keys.
In the mean time primary key of other tables like OptionValue is a composite key (OptionId + ValueName)
It looks like life will be more easy to have an ID field in every table as the primary key, yes it is but as a database designer you will loos something valuable, business logic.

In the current design you can have these constraint in Product-Specification table, they will show part of your business logic:

  • Check constraint on ProductSpecification {OptionValue.optionId = productTypeOption.optionId} that will prevent a value like "White" being assigned to "Size".
  • Check constraint on ProductSpecification {product.productTypeId = productTypeOption.productTypeId} that will prevent a product like "Nike" being assigned to productSpecifications of "Cars".

If you use surrogate identifier you can not have these type of constraints inside your data base (try this).
Extra work will be needed to be done inside you application implementation to gain them.
BTW use surrogate identifier, check data consistency, if more interested see choosing a Primary Key: Natural or Surrogate.

Where should the base price, stock, and surcharge go?

It seems that "Mens Shoe" of "Nike" needs to have price, stock and surcharge, so they are natural property of Product table.

Community
  • 1
  • 1
Mohsen Heydari
  • 7,256
  • 4
  • 31
  • 46
  • Would you mind please providing a real world example on how this schema would be implemented? I'm not entirely sure by the diagram alone how the tables relate, or where you would put the stock quantities in relation to the product options. – mister martin Jan 14 '14 at 14:34
  • 1
    I have added some sqlfiddle demo, hop this helps. – Mohsen Heydari Jan 15 '14 at 07:18
  • Thank you for the example, I have a few more questions. I've converted your example to match [my code](http://sqlfiddle.com/#!2/4b111/1). #1 Could I rename the product_type table to product_categories and add a parent ID, so a single product could belong to multiple categories? #2 You put the "surcharge" and "stock" in the products table, but the date in the specs table, where does the other product information go such as base price, weight, SKU, etc? #3 Since there is no unique product ID, am I restricted to using only the product titles in my queries? Or how can I add a unique product ID? – mister martin Jan 15 '14 at 19:29
  • 1
    **#1** _product_type_ and _Spec_ tables are where composition of options occur's, it is a design level concept, I prefer not to use it as a business level table, category is a business concept so create a new table called categories, based on the business role, a many-to-many table product_category or product_type_category will serve the purpose. – Mohsen Heydari Jan 15 '14 at 21:33
  • 1
    **#2** The model i have suggested is just a _draft to visualize the design_, feel free to extend it, as you mentioned about product table, that must be extended. **Note:** The date fields inside Spec table are dates of when an option is added (deleted) to a product type (for history usage), they are not business fields. – Mohsen Heydari Jan 15 '14 at 21:34
  • How about #3? Is it possible to have a unique product ID? And I'm still confused, where should the base price, stock, and surcharge go? I really appreciate your help. – mister martin Jan 16 '14 at 01:34
  • 1
    I've edited my original question with an explanation of your answer. You're awesome, thank you very much! – mister martin Jan 16 '14 at 14:02
0

Here's something to get you started...

Every unique type of item should have it's own SKU (look at example 4) so you could design your database like this:

colors
    id              unsigned int(P)
    description     varchar(10)

+----+-------------+
| id | description |
+----+-------------+
|  1 | White       |
|  2 | Blue        |
|  3 | Green       |
| .. | ........... |
+----+-------------+

items
    id              unsigned int(P)
    description     varchar(20)

+----+-------------+
| id | description |
+----+-------------+
|  1 | T-shirt     |
|  2 | Pencil      |
| .. | ........... |
+----+-------------+

sizes
    id              unsigned int(P)
    description     varchar(10)

+----+-------------+
| id | description |
+----+-------------+
|  1 | Small       |
|  2 | Medium      |
|  3 | Large       |
| .. | ........... |
+----+-------------+

In my example data below SKU S1C1I1 is a small, white T-shirt and S2C3I1 is a medium green T-shirt.

products
    id              unsigned int(P)
    sku             varchar(50)
    price           double
    quantity        unsigned int

+----+--------+-------+----------+
| id | sku    | price | quantity |
+----+--------+-------+----------+
|  1 | S1C1I1 | 10.00 |      312 |
|  2 | S2C3I1 | 11.00 |       52 |
| .. | ...... | ..... | ........ |
+----+--------+-------+----------+

You might also have UPC, EAN, etc. in your products table.

Benny Hill
  • 6,191
  • 4
  • 39
  • 59
  • Thanks for the feedback, although I'm not sure I understand how a SKU has anything to do with storing data as those can change between company who might define their own. It isn't exactly flexible. You've also hard-coded your sizes and colors into individual tables, but as per my example I may not know ahead of time what those options and associated properties will be. – mister martin Jan 09 '14 at 16:26
  • @mistermartin - You need to add some details to your question. For instance you should add that you are wanting to track products from multiple companies other than your own. How are you obtaining their stock information? Instead of a SKU you could use a UPC or EAN code because those are unique, even across multiple companies. – Benny Hill Jan 09 '14 at 16:47
  • If a company is selling a custom product it may not have a UPC or EAN. I really don't think the details you're asking for are relevant to the question, but I am open minded and willing to listen if you think I'm missing the point you're trying to make. The question is how to track stock inventory with multiple options without knowing ahead of time what those options are. My schema obviously doesn't work; what will work? It doesn't matter where the stock information is coming from, only how it is stored. The SKU, UPC, EAN etc should all be optional as far as I'm concerned. – mister martin Jan 09 '14 at 17:07