从库存数据库有效查询产品变体(避免使用EAV)

时间:2018-09-07 07:55:17

标签: mysql join database-design many-to-many

我正在构建一个有趣的电子商务网站,其中包含三个主要组件:Angular中的前端,MySQL数据库和将用作REST API的PHP后端。

库存数据库由产品组成,产品具有多种变体(是的,我在StackExchange上已经看到许多讨论此类问题的问题,但我还没有发现令人满意的灵魂)。在阅读了一些使用EAV方法的恐怖故事后,我避免了。

我认为我已经制定了一种行之有效的数据库解决方案,但不确定如何查询数据。由于存在几种多对多关系,对一种产品及其所有变体的查询可以返回几行,其中只有几列包含“新”数据。

(TL:DR)主要问题:在查询产品的所有变体(颜色,可用颜色的大小以及颜色/大小的变体的数量)时,最有效的方法是:

  • 执行1个数据库查询,返回很多行,我需要在PHP中建立一个关联数组,并丢弃“重复的”数据?

  • 检索产品的所有变体时执行几个查询?

  • 还是数据库设计中的缺陷是导致这些问题的原因?

数据库架构:

CREATE TABLE IF NOT EXISTS `products` (
  `productID` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(100) NOT NULL,
  `longDescription` TEXT,
  `shortDescription` VARCHAR(1000),
  PRIMARY KEY (`productID`)
);
INSERT INTO `products` (`productID`, `name`, `longDescription`, `shortDescription`) VALUES 
(1, 'A shirt', 'Long description of this product', 'shortDesc of shirt');

CREATE TABLE IF NOT EXISTS `productpricing` (
  `productID` INT(11) NOT NULL,
  `startDate` TIMESTAMP NOT NULL DEFAULT '1971-01-01 00:00:00',
  `endDate` TIMESTAMP NOT NULL DEFAULT '2099-01-01 00:00:00',
  `price` DECIMAL(10, 2) NOT NULL,
  PRIMARY KEY(`productID`, `endDate`),
  FOREIGN KEY (`productID`) REFERENCES products(`productID`) 
);
INSERT INTO `productpricing` (`productID`, `startDate`, `endDate`, `price`) VALUES
(1, '1971-01-01 00:00:00', '2099-01-01 00:00:00', 309.99);

CREATE TABLE IF NOT EXISTS `categories` (
  `categoryID` INT(11) NOT NULL AUTO_INCREMENT,
  `categoryName` VARCHAR(100) NOT NULL,
  PRIMARY KEY (`categoryID`)
);
INSERT INTO `categories` (`categoryID`, `categoryName`) VALUES
(1, 'Test Category');

CREATE TABLE IF NOT EXISTS `sizes` (
  `sizeID` INT(11) NOT NULL,
  `size` INT NOT NULL,
  PRIMARY KEY (`sizeID`)
);
INSERT INTO `sizes` (`sizeID`, `size`) VALUES
(1, 50),
(2, 56),
(3, 62),
(4, 68),
(5, 74),
(6, 80),
(7, 86);

CREATE TABLE IF NOT EXISTS `colors` (
  `colorID` INT(11) NOT NULL,
  `color` VARCHAR(100) NOT NULL,
  PRIMARY KEY (`colorID`)
);
INSERT INTO `colors` (`colorID`, `color`) VALUES
(1, "Red"),
(2, "White"),
(3, "Blue"),
(4, "Purple");

CREATE TABLE IF NOT EXISTS `product_variants` (
  `productvariantID` INT(11) NOT NULL,
  `productID` INT(11) NOT NULL,
  `categoryID` INT(11) NOT NULL,
  `colorID` INT(11) NOT NULL,
  `sizeID` INT(11) NOT NULL,
  `sku` VARCHAR(50) NOT NULL UNIQUE,
  `quantity` INT(11) NOT NULL,
  `isActive` BOOLEAN NOT NULL DEFAULT 0,
  PRIMARY KEY (`productvariantID`),
  UNIQUE (`productID`, `colorID`, `sizeID`),
  FOREIGN KEY (`productID`) REFERENCES products(`productID`),
  FOREIGN KEY (`categoryID`) REFERENCES categories(`categoryID`),
  FOREIGN KEY (`colorID`) REFERENCES colors(`colorID`),
  FOREIGN KEY (`sizeID`) REFERENCES sizes(`sizeID`)
);
INSERT INTO `product_variants` (`productvariantID`, `productID`, `categoryID`, `colorID`, `sizeID`, `sku`, `quantity`, `isActive`) VALUES
(1, 1, 1, 2, 1, 'clalb121', 2, 1),
(2, 1, 1, 3, 2, 'clalb132', 1, 1),
(3, 1, 1, 2, 2, 'clalb122', 5, 1);

CREATE TABLE IF NOT EXISTS `images` (
  `imageID` INT(11) NOT NULL AUTO_INCREMENT,
  `imageFilename` VARCHAR(100) NOT NULL,
  PRIMARY KEY(`imageID`)
);
INSERT INTO `images` (`imageID`, `imageFilename`) VALUES
(1, 'shirtwhite1.jpg'),
(2, 'shirtwhite2.jpg'),
(3, 'shirtblue1.jpg'),
(4, 'shirtblue2.jpg');

CREATE TABLE IF NOT EXISTS `product_variant_images` (
  `productvariantID` INT(11) NOT NULL,
  `imageID` INT(11) NOT NULL,
  FOREIGN KEY(`productvariantID`) REFERENCES  product_variants(`productvariantID`),
  FOREIGN KEY(`imageID`) REFERENCES images(`imageID`)
);
INSERT INTO `product_variant_images` (`productvariantID`, `imageID`) VALUES
(1, 1),
(1, 2),
(2, 3),
(2, 4),
(3, 1),
(3, 2);

(由于同一产品的不同变体没有不同的类别,因此我将从 product_variants 表中删除 categoryID 。)

查询1种产品的所有变体的示例(在一个查询中):

SELECT
    p.productID,
    p.name,
    p.longDescription,
    p.shortDescription,
    colors.color,
    sizes.size,
    pvar.quantity,
    pprice.price,
    images.imageFilename
FROM products as p
JOIN product_variants as pvar
     ON p.productID = pvar.productID
JOIN productpricing as pprice
    ON pprice.productID = p.productID
JOIN colors 
    ON colors.colorID = pvar.colorID
JOIN sizes
    ON sizes.sizeID = pvar.sizeID
JOIN product_variant_images as pvari
    ON pvari.productvariantID = pvar.productvariantID
JOIN images 
    ON images.imageID = pvari.imageID
WHERE p.productID = 1 AND pvar.isActive = 1 AND NOW() BETWEEN pprice.startDate AND pprice.endDate;

示例返回集:

# productID, name, longDescription, shortDescription, color, size, quantity, price, imageFilename
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '50', '2', '309.99', 'shirtwhite1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '50', '2', '309.99', 'shirtwhite2.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '56', '5', '309.99', 'shirtwhite1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '56', '5', '309.99', 'shirtwhite2.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'Blue', '56', '1', '309.99', 'shirtblue1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'Blue', '56', '1', '309.99', 'shirtblue2.jpg'

从结果集中可以看到,衬衫的白色变体(一种尺寸)与两个图像相关联,因此有两个记录只有 imageFilename 包含“新数据”。 / p>

使用多个查询的示例:

SELECT 
    p.productID,
    p.name,
    p.longDescription,
    p.shortDescription,
    pprice.price
FROM products as p
JOIN productpricing as pprice
    ON pprice.productID = p.productID
WHERE p.productID = 1 AND NOW() BETWEEN pprice.startDate AND pprice.endDate;

SELECT
    c.color,
    s.size,
    pvar.quantity
FROM product_variants as pvar
JOIN colors AS c
    ON c.colorID = pvar.colorID
JOIN sizes as s
    ON s.sizeID = pvar.sizeID
WHERE pvar.productID = 1;

SELECT DISTINCT
    c.color,
    i.imageFilename
FROM images  AS i
JOIN product_variant_images as pvari
    ON pvari.imageID = i.imageID
JOIN product_variants as pvar
    ON pvar.productvariantID = pvari.productvariantID
JOIN colors AS c
    ON c.colorID = pvar.colorID
WHERE pvar.productID = 1;

几个查询结果集:

# productID, name, longDescription, shortDescription, price
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', '309.99'

# color, size, quantity
'White', '50', '2'
'Blue', '56', '1'
'White', '56', '5'

# color, imageFilename
'White', 'shirtwhite1.jpg'
'White', 'shirtwhite2.jpg'
'Blue', 'shirtblue1.jpg'
'Blue', 'shirtblue2.jpg'

非常感谢您提供有关数据库设计/查询的建议!

0 个答案:

没有答案