简体   繁体   中英

Inventory management with stock options

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.

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

在此输入图像描述

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM