1. ホーム
  2. mysql

製品バリエーションのモデリング

2023-07-21 06:08:26

質問

製品のバリアントをモデル化しようとしていて、EAV を使用する必要があるかもしれないと思いました。EAV なしでもできたかもしれませんが、何か見落としているのではないかと心配しています。以下は私のデザインです。

私が表現しようとしているのは、こんな感じです。

  1. A product は0個以上の product variants を持つことができます (例: T シャツ製品はサイズと色のバリエーションがあります)。
  2. A product variant は1つまたはそれ以上の product variant options を持つことができます (例えば、サイズのバリエーションは、小、中、大になります)。
  3. An SKU は、1つまたは複数の product variant options (その product_variant_option_combination テーブルには、`product_variant_options' のすべての可能な組み合わせが含まれます。つまり、3 つのサイズと 3 つの色があった場合、3 * 3 = 9 の組み合わせがあり、それぞれの組み合わせに独自の SKU と価格が与えられます)。
  4. A product は1つまたはそれ以上の SKUs .

製品にバリアントがない場合は、単に product_variants , product_variant_options そして product_variant_option_combinations .

この設計は健全でしょうか?このクエリで問題が発生することはないでしょうか?拡張性はありますか?正規化されていますか?

アップデイト1

@Edper です。

もし製品が0個または多数の(オプションモードの)製品バリエーション(例:サイズ、色など)を持つことができる場合。製品のバリアントも、そのバリアントを持つ 0 個または多数の製品を持つことができるということになりますか?

そうではありません。Tシャツなどの商品にサイズバリエーションがあり、パンツなどの商品にもサイズバリエーションがあることはあり得ますが、それはたまたまだと思います。サイズバリエーションが異なるからと言って、1つのレコードとしてしか表示されないようにする必要はありません。

私が扱っている製品は非常にさまざまで、似たような名前のバリエーションがあるに違いありません。

UPDATE 2:

私のデータの見方の一例です。

バリアントをボックス化した Size とそれに関連する値を囲みました。これらが重複したデータとは見なされないことを明確にしたいのです。その Size の変種が3つの製品にあるのは、単なる偶然です。これを正規化する必要はない、と私は思います。各製品は 0 以上の variant を持つことができ、それらは私には未知です。私は、quot;duplicates" を期待します(ただし、特定の製品のコンテキストでは常にそうなので、実際には重複していません -- つまり、Widget 1 の "Size" variant は Widget 2 の "Size" variant と同じというわけではありません)。

アップデイト 3:

私のデザインでは、このように product に複数の同じ product_variants . とすることで、解決できると思います。 product_variants . product_id そして product_variants . name は複合キーです。これは、Widget 1 が "Size" のバリアントを一度だけ持つことができることを意味します。

product_variant_options . product_variant_id product_variant_options . name も複合キーである必要があります。

UPDATE 4:

を更新することで product_variant_option_combinations をインクルードすることで product_variant_id (FKから product_variants . id でUNIQUE制約を強制します。 product_variant_option_combinations . sku_id そして product_variant_option_combinations . product_variant_id これで、SKUがSmallとLargeに分かれてしまう問題を回避できたと思います。これでいいのでしょうか?

-- phpMyAdmin SQL Dump
-- version 4.1.14
-- http://www.phpmyadmin.net
--
-- Host: 127.0.0.1
-- Generation Time: Jul 30, 2014 at 03:35 AM
-- Server version: 5.6.17
-- PHP Version: 5.5.12

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

--
-- Database: `mydb`
--

-- --------------------------------------------------------

--
-- Table structure for table `products`
--

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

--
-- Dumping data for table `products`
--

INSERT INTO `products` (`id`, `name`) VALUES
(1, 'Widget 1');

-- --------------------------------------------------------

--
-- Table structure for table `product_variants`
--

CREATE TABLE IF NOT EXISTS `product_variants` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_product_id_name` (`product_id`,`name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

--
-- Dumping data for table `product_variants`
--

INSERT INTO `product_variants` (`id`, `product_id`, `name`) VALUES
(2, 1, 'Color'),
(1, 1, 'Size');

-- --------------------------------------------------------

--
-- Table structure for table `product_variant_options`
--

CREATE TABLE IF NOT EXISTS `product_variant_options` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_variant_id` int(11) NOT NULL,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_product_variant_id_name` (`product_variant_id`,`name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

--
-- Dumping data for table `product_variant_options`
--

INSERT INTO `product_variant_options` (`id`, `product_variant_id`, `name`) VALUES
(2, 1, 'Large'),
(1, 1, 'Small'),
(4, 2, 'Black'),
(3, 2, 'White');

-- --------------------------------------------------------

--
-- Table structure for table `skus`
--

CREATE TABLE IF NOT EXISTS `skus` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `sku` varchar(45) NOT NULL,
  `price` decimal(10,2) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `skus_product_id_products_id_idx` (`product_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

--
-- Dumping data for table `skus`
--

INSERT INTO `skus` (`id`, `product_id`, `sku`, `price`) VALUES
(1, 1, 'W1SSCW', '10.00'),
(2, 1, 'W1SSCB', '10.00'),
(3, 1, 'W1SLCW', '12.00'),
(4, 1, 'W1SLCB', '15.00');

-- --------------------------------------------------------

--
-- Table structure for table `skus_product_variant_options`
--

CREATE TABLE IF NOT EXISTS `skus_product_variant_options` (
  `sku_id` int(11) NOT NULL,
  `product_variant_id` int(11) NOT NULL,
  `product_variant_options_id` int(11) NOT NULL,
  PRIMARY KEY (`sku_id`,`product_variant_options_id`,`product_variant_id`),
  UNIQUE KEY `UNIQUE_sku_id_product_variant_id` (`sku_id`,`product_variant_id`),
  KEY `spvo_product_variant_options_id_pro_idx` (`product_variant_options_id`),
  KEY `spvo_product_variant_id_product_var_idx` (`product_variant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `skus_product_variant_options`
--

INSERT INTO `skus_product_variant_options` (`sku_id`, `product_variant_id`, `product_variant_options_id`) VALUES
(1, 1, 1),
(2, 1, 1),
(3, 1, 2),
(4, 1, 2),
(1, 2, 3),
(3, 2, 3),
(2, 2, 4),
(4, 2, 4);

--
-- Constraints for dumped tables
--

--
-- Constraints for table `product_variants`
--
ALTER TABLE `product_variants`
  ADD CONSTRAINT `product_variants_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;

--
-- Constraints for table `product_variant_options`
--
ALTER TABLE `product_variant_options`
  ADD CONSTRAINT `product_variant_options_product_variant_id_product_variants_id` FOREIGN KEY (`product_variant_id`) REFERENCES `product_variants` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;

--
-- Constraints for table `skus`
--
ALTER TABLE `skus`
  ADD CONSTRAINT `skus_product_id_products_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;

--
-- Constraints for table `skus_product_variant_options`
--
ALTER TABLE `skus_product_variant_options`
  ADD CONSTRAINT `skus_product_variant_options_sku_id_skus_id` FOREIGN KEY (`sku_id`) REFERENCES `skus` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  ADD CONSTRAINT `spvo_product_variant_options_id_product_variant_options_id` FOREIGN KEY (`product_variant_options_id`) REFERENCES `product_variant_options` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  ADD CONSTRAINT `spvo_product_variant_id_product_variants_id` FOREIGN KEY (`product_variant_id`) REFERENCES `product_variants` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

どのように解決するのですか?

のようなデザインにすればよいでしょう。

 +---------------+     +-------------------+
 | PRODUCTS      |-----< PRODUCT_VARIANTS  |
 +---------------+     +-------------------+
 | #product_id   |     | #product_id       |
 |  product_name |     | #variant_id       |
 +---------------+     |  sku_id           |
         |             +-------------------+
         |                       |
+--------^--------+     +--------^--------+
| PRODUCT_OPTIONS |-----< VARIANT_VALUES  |
+-----------------+     +-----------------+
| #product_id     |     | #product_id     |
| #option_id      |     | #variant_id     |
+--------v--------+     | #option_id      |
         |              |  value_id       |
+-----------------+     +--------v--------+
| OPTIONS         |              |
+-----------------+              |
| #option_id      |              |
|  option_name    |              |
+-----------------+              |
         |                       |
 +-------^-------+               |
 | OPTION_VALUES |---------------+
 +---------------+
 | #option_id    |
 | #value_id     |
 |  value_name   |
 +---------------+

主キー、ユニークキー、外部キーと。

  • 製品
    • PK: product_id
    • UK: 製品名
  • オプション
    • PK: オプションID
    • UK: オプション名
  • OPTION_VALUES
    • PK: option_id, value_id
    • UK: オプションID, 値の名前
    • FK: option_id REFERENCES OPTIONS (option_id) です。
  • product_options(製品オプション
    • PK: product_id、option_id。
    • FK: product_id REFERENCES PRODUCTS (product_id)。
    • FK: option_id REFERENCES OPTIONS (option_id) を参照してください。
  • 製品バリエーション
    • PK: product_id, variant_id
    • UK: sku_id
    • FK: product_id REFERENCES PRODUCTS (product_id) を参照してください。
  • VARIANT_VALUES
    • PK: product_id、variant_id、option_id。
    • FK: product_id, variant_id REFERENCES PRODUCT_VARIANTS (product_id, variant_id) です。
    • FK: product_id, option_id REFERENCES PRODUCT_OPTIONS (product_id, option_id)
    • FK: option_id, value_id REFERENCES OPTION_VALUES (option_id, value_Id)

あなたは持っています。

  • 製品 例:シャツ、ジャンパー、ズボン
  • オプション 例:サイズ、カラー、丈など
  • OPTION_VALUES 例:サイズ - スモール、ミディアム、ラージ、カラー - レッド、ホワイト、ブルー
  • Product_OPTIONS 例:シャツ - サイズ、カラー;ズボン - 長さ、カラー

次に、商品のオプションの数と同じ次元数の n 次元配列を作成する必要があります。配列の各要素は、製品のバリエーションに対応します。各製品には必ず少なくとも 1 つの製品バリアントがあり、製品の "as-is" という擬似オプションが常にあるためです。

  • PRODUCT_VARIANTS 例: シャツ1、シャツ2
  • VARIANT_VALUES 例:シャツ1:赤小、シャツ2:白小

商品に関連するすべてのオプションに値が指定されていない限り、SKUが割り当てられないことを確認するために、検証を行うことを望むかもしれません。

データをどのように見るかのスプレッドシートに基づいて、次のようにテーブルにデータを入力することができます。

PRODUCTS
========
id  name
--- --------
1   Widget 1
2   Widget 2
3   Widget 3
 
PRODUCT_VARIANTS
================
id  product_id name
--- ---------- ------
1   1          Size   (Widget 1)
2   1          Color  (Widget 1)
3   2          Size   (Widget 2)
4   3          Class  (Widget 3)
5   3          Size   (Widget 3)
 
PRODUCT_VARIANT_OPTIONS
=======================
id  product_variant_id name
--- ------------------ -------------
1   1                  Small         (Widget 1; Size)
2   1                  Large         (Widget 1; Size)
3   2                  White         (Widget 1; Color)
4   2                  Black         (Widget 1; Color)
5   3                  Small         (Widget 2; Size)
6   3                  Medium        (Widget 2; Size)
7   4                  Amateur       (Widget 3; Class)
8   4                  Professional  (Widget 3; Class)
9   5                  Medium        (Widget 3; Size)
10  5                  Large         (Widget 3; Size)
 
SKUS
====
id  product_id sku    price
--- ---------- ------ -----
1   1          W1SSCW    10 (Widget 1)
2   1          W1SSCB    10 (Widget 1)
3   1          W1SLCW    12 (Widget 1)
4   1          W1SLCB    15 (Widget 1)
5   2          W2SS     100 (Widget 2)
6   2          W2SM     100 (Widget 2)
7   3          W3CASM    50 (Widget 3)
8   3          W3CASL    50 (Widget 3)
9   3          W3CPSM   150 (Widget 3)
10  3          W3CPSL   160 (Widget 3)
 
PRODUCT_VARIANT_OPTION_COMBINATIONS
===================================
product_variant_option_id sku_id
------------------------- ------
1                         1      (W1SSCW; Size; Small)
3                         1      (W1SSCW; Color; White)
1                         2      (W1SSCB; Size; Small)
4                         2      (W1SSCB; Color; Black)
2                         3      (W1SLCW; Size; Large)
3                         3      (W1SLCW;  Color; White)
2                         4      (W1SLCB; Size; Large)
4                         4      (W1SLCB; Color; Black)
5                         5      (W2SS; Size; Small)
6                         6      (W2SM; Size; Medium)
7                         7      (W3CASM; Class; Amateur)
9                         7      (W3CASM; Size; Medium)
7                         8      (W3CASL; Class; Amateur)
10                        8      (W3CASL; Size; Large)
8                         9      (W3CPSM; Class; Professional)
9                         9      (W3CPSM; Size; Medium)
8                         10     (W3CPSL; Class; Professional)
10                        10     (W3CPSL; Size; Large)

SKU W1SSCW が Small と Large の両方のオプションを持つように、レコード (product_variant_option_id: 2; sku_id 1) のエントリの追加を停止するデザインは何もないように思われます。レコード (product_variant_option_id: 7; sku_id: 1) のエントリを停止するものは何もないので、SKU W1SSCW は Amateur というオプションも持っています。

データをどのように見るかのスプレッドシートに基づいて、次のように私のテーブルにデータを入力することができます。

PRODUCTS
========
product_id product_name
---------- ------------
1          Widget 1
2          Widget 2
3          Widget 3
 
OPTIONS
=======
option_id option_name
--------- -----------
1         Size SL
2         Color
3         Size SM
4         Class
5         Size ML
 
OPTION_VALUES
=============
option_id value_id value_name
--------- -------- ------------
1         1        Small        (Size SL)
1         2        Large        (Size SL)
2         1        White        (Color)
2         2        Black        (Color)
3         1        Small        (Size SM)
3         2        Medium       (Size SM)
4         1        Amateur      (Class)
4         2        Professional (Class)
5         1        Medium       (Size ML)
5         2        Large        (Size ML)
 
PRODUCT_OPTIONS
===============
product_id option_id
---------- ---------
1          1         (Widget 1; Size SL)
1          2         (Widget 1; Color)
2          3         (Widget 2; Size SM)
3          4         (Widget 3; Class)
3          5         (Widget 4; Size ML)
 
PRODUCT_VARIANTS
================
product_id variant_id sku_id
---------- ---------- ------
1          1          W1SSCW (Widget 1)
1          2          W1SSCB (Widget 1)
1          3          W1SLCW (Widget 1)
1          4          W1SLCB (Widget 1)
2          1          W2SS   (Widget 2)
2          2          W2SM   (Widget 2)
3          1          W3CASM (Widget 3)
3          2          W3CASL (Widget 3)
3          3          W3CPSM (Widget 3)
3          4          W3CPSL (Widget 3)
 
VARIANT_VALUES
==============
product_id variant_id option_id value_id
---------- ---------- --------- --------
1          1          1         1        (W1SSCW; Size SL; Small)
1          1          2         1        (W1SSCW; Color; White)
1          2          1         1        (W1SSCB; Size SL; Small)
1          2          2         2        (W1SSCB; Color; Black)
1          3          1         2        (W1SLCW; Size SL; Large)
1          3          2         1        (W1SLCW; Color; White)
1          4          1         2        (W1SLCB; Size SL; Large)
1          4          2         2        (W1SLCB; Color; Black)
2          1          3         1        (W2SS; Size SM; Small)
2          2          3         2        (W2SM; Size SM; Medium)
3          1          4         1        (W3CASM; Class; Amateur)
3          1          5         1        (W3CASM; Size ML; Medium)
3          2          4         1        (W3CASL; Class; Amateur)
3          2          5         2        (W3CASL; Size ML; Large)
3          3          4         2        (W3CPSM; Class; Professional)
3          3          5         1        (W3CPSM; Size ML; Medium)
3          4          4         2        (W3CPSL; Class; Professional)
3          4          5         2        (W3CPSL; Size ML; Large)

私のデザインでは、追加の VARIANT_VALUES レコード (product_id: 1; variant_id: 1; option_id: 1; value_id: 2) を入力することができませんでした。そのため、VARIANT_VALUES と既存の VARIANT_VALUES レコード (product_id: 1; variant_id: 1; option_id: 1) の主キーにより SKU W1SSCW には Small と Large という両方のオプションが付いています。私のデザインでは、VARIANT_VALUESレコード(product_id: 1; variant_id: 1; option_id: 4; value_id: 1)を入力できず、SKU W1SSCWにもAmateurというオプションがありますが、これは、PRODUCT_OPTIONSへの外部キーとこのテーブル内にClassが製品Widget 1の有効なオプションであることを示す(product_id: 1; option_id: 4)という記録がないことが理由です。

EDIT : PRODUCT_OPTIONSテーブルがないデザイン

のようなデザインにすることができます。

+---------------+     +---------------+
| PRODUCTS      |-----< PRODUCT_SKUS  |
+---------------+     +---------------+
| #product_id   |     | #product_id   |
|  product_name |     | #sku_id       |
+---------------+     |  sku          |
        |             |  price        |
        |             +---------------+
        |                     |
+-------^-------+      +------^------+
| OPTIONS       |------< SKU_VALUES  |
+---------------+      +-------------+
| #product_id   |      | #product_id |
| #option_id    |      | #sku_id     |
|  option_name  |      | #option_id  |
+---------------+      |  value_id   |
        |              +------v------+
+-------^-------+             |
| OPTION_VALUES |-------------+
+---------------+
| #product_id   |
| #option_id    |
| #value_id     |
|  value_name   |
+---------------+

主キー、ユニークキー、外部キーと。

  • 製品
    • PK: product_id
    • UK: 製品名
  • オプション
    • PK: product_id, option_id
    • UK:プロダクトID、オプション名
  • OPTION_VALUES
    • PK: product_id、option_id、value_id。
    • UK: product_id、option_id、value_name。
    • FK: product-id, option_id REFERENCES OPTIONS (product_id, option_id).
  • PRODUCT_SKUS
    • PK: product_id、sku_id。
    • UK: sku_id
    • FK: product_id REFERENCES PRODUCTS (product_id) を参照してください。
  • SKU_VALUES
    • PK: product_id、sku_id、option_id。
    • FK: product_id, sku_id REFERENCES PRODUCT_SKUS (product_id, sku_id)
    • FK: product_id, option_id REFERENCES OPTIONS (product_id, option_id)
    • FK: product_id, option_id, value_id REFERENCES OPTION_VALUES (product_id, option_id, value_id)

データの見方のスプレッドシートに基づいて、これらのテーブルに以下のようにデータを入力することができます。

PRODUCTS
========
product_id product_name
---------- ------------
1          Widget 1
2          Widget 2
3          Widget 3
 
OPTIONS
=======
product_id option_id option_name
---------- --------- -----------
1          1         Size        (Widget 1)
1          2         Color       (Widget 1)
2          1         Size        (Widget 2)
3          1         Class       (Widget 3)
3          2         Size        (Widget 3)
 
OPTION_VALUES
=============
product_id option_id value_id value_name
---------- --------- -------- ------------
1          1         1        Small        (Widget1; Size)
1          1         2        Large        (Widget1; Size)
1          2         1        White        (Widget1; Color)
1          2         2        Black        (Widget1; Color)
2          1         1        Small        (Widget2; Size)
2          1         2        Medium       (Widget2; Size)
3          1         1        Amateur      (Widget3; Class)
3          1         2        Professional (Widget3; Class)
3          2         1        Medium       (Widget3; Size)
3          2         2        Large        (Widget3; Size)
 
PRODUCT_SKUS
============
product_id sku_id sku
---------- ------ ------
1          1      W1SSCW (Widget 1)
1          2      W1SSCB (Widget 1)
1          3      W1SLCW (Widget 1)
1          4      W1SLCB (Widget 1)
2          1      W2SS   (Widget 2)
2          2      W2SM   (Widget 2)
3          1      W3CASM (Widget 3)
3          2      W3CASL (Widget 3)
3          3      W3CPSM (Widget 3)
3          4      W3CPSL (Widget 3)
 
SKU_VALUES
==========
product_id sku_id option_id value_id
---------- ------ --------- --------
1          1      1         1        (W1SSCW; Size; Small)
1          1      2         1        (W1SSCW; Color; White)
1          2      1         1        (W1SSCB; Size; Small)
1          2      2         2        (W1SSCB; Color; Black)
1          3      1         2        (W1SLCW; Size; Large)
1          3      2         1        (W1SLCW; Color; White)
1          4      1         2        (W1SLCB; Size; Large)
1          4      2         2        (W1SLCB; Color; Black)
2          1      1         1        (W2SS; Size; Small)
2          2      1         2        (W2SM; Size; Medium)
3          1      1         1        (W3CASM; Class; Amateur)
3          1      2         1        (W3CASM; Size; Medium)
3          2      1         1        (W3CASL; Class; Amateur)
3          2      2         2        (W3CASL; Size; Large)
3          3      1         2        (W3CPSM; Class; Professional)
3          3      2         1        (W3CPSM; Size; Medium)
3          4      1         2        (W3CPSL; Class; Professional)
3          4      2         2        (W3CPSL; Size; Large)