本文目录导读:

- 方案一:标准SKU(SPU + SKU 模式)—— 最推荐
- 方案二:简化版(单表)—— 适合小型项目
- 方案三:极简字符串拼键(HashMap思路)
- 方案四:EAV表结构(最灵活,适合超多规格或自定义属性)
- 关键设计考虑点
- 总结推荐
设计商品SKU(Stock Keeping Unit,库存量单位)是电商系统的核心,PHP后端设计表时,关键在于数据结构要支持多规格组合、库存管理和价格区分。
以下是电商系统中常用的SKU设计方案,从标准方案到进阶优化,附带MySQL建表语句和PHP逻辑示例。
标准SKU(SPU + SKU 模式)—— 最推荐
这是目前最主流的方案,核心思路是:SPU(商品)表 存商品公共信息,SKU表 存具体的规格和库存。
商品表 (spu)
CREATE TABLE `spu` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '商品ID', VARCHAR(200) NOT NULL COMMENT '商品标题', `sub_title` VARCHAR(500) DEFAULT '' COMMENT '副标题/卖点', `main_image` VARCHAR(255) NOT NULL COMMENT '主图URL', `status` TINYINT NOT NULL DEFAULT '1' COMMENT '状态:1上架,0下架', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
规格属性组表 (spec_group) —— 可选,用于分类管理
CREATE TABLE `spec_group` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `spu_id` INT UNSIGNED NOT NULL COMMENT '所属商品ID', `spec_name` VARCHAR(50) NOT NULL COMMENT '规格名,如:颜色、尺码', PRIMARY KEY (`id`), KEY `idx_spu` (`spu_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
规格属性值表 (spec_value) —— 可选,用于记录该商品的规格选项
CREATE TABLE `spec_value` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `group_id` INT UNSIGNED NOT NULL COMMENT '所属规格组ID', `value_name` VARCHAR(50) NOT NULL COMMENT '规格值,如:红色、XL', PRIMARY KEY (`id`), KEY `idx_group` (`group_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心:SKU库存表 (sku) —— 最关键的表
CREATE TABLE `sku` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`spu_id` INT UNSIGNED NOT NULL COMMENT '所属SPU ID',
`sku_code` VARCHAR(32) NOT NULL COMMENT '商家编码/货号',
`spec_info` JSON NOT NULL COMMENT '规格信息,JSON格式,例:{"颜色":"红色","尺码":"XL"}',
`price` DECIMAL(10,2) NOT NULL COMMENT '售价',
`cost_price` DECIMAL(10,2) DEFAULT '0.00' COMMENT '成本价',
`stock` INT NOT NULL DEFAULT '0' COMMENT '库存数量',
`alert_stock` INT DEFAULT '0' COMMENT '库存预警值',
`sku_image` VARCHAR(255) DEFAULT '' COMMENT 'SKU专属图片(可选)',
`weight` DECIMAL(8,2) DEFAULT '0.00' COMMENT '重量(kg)',
`status` TINYINT NOT NULL DEFAULT '1' COMMENT '状态:1启用,0停用',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_spu_spec` (`spu_id`, `spec_info`), -- 注意评估是否使用JSON唯一索引(MySQL 8.0支持),或使用序列化字符串
KEY `idx_spu` (`spu_id`),
KEY `idx_code` (`sku_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='SKU库存表';
PHP处理逻辑示例:
<?php // 假设前端传来:颜色=红色,尺码=XL $specs = ['颜色' => '红色', '尺码' => 'XL']; // 存入数据库时,序列化为JSON $spec_json = json_encode($specs, JSON_UNESCAPED_UNICODE); // 查询时,根据规格组合获取SKU $query = "SELECT * FROM sku WHERE spu_id = ? AND spec_info = ?"; $stmt = $pdo->prepare($query); $stmt->execute([$spu_id, $spec_json]); $sku = $stmt->fetch(); ?>
简化版(单表)—— 适合小型项目
如果规格较少(例如只有1-2个维度),将规格直接作为字段存入商品表。
商品表 (product)
CREATE TABLE `product` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(200) NOT NULL, `spec_type` VARCHAR(50) DEFAULT NULL COMMENT '规格1名称,如:颜色', `spec_value1` VARCHAR(50) DEFAULT NULL COMMENT '规格1值,如:红色', `spec_type2` VARCHAR(50) DEFAULT NULL COMMENT '规格2名称,如:尺码', `spec_value2` VARCHAR(50) DEFAULT NULL COMMENT '规格2值,如:XL', `price` DECIMAL(10,2) NOT NULL, `stock` INT NOT NULL, `image` VARCHAR(255) DEFAULT '', -- 其余字段... KEY `idx_spec1` (`spec_value1`), KEY `idx_spec2` (`spec_value2`) ) ENGINE=InnoDB;
缺点:无法灵活扩展规格数量;查询时条件复杂;库存管理困难,仅适合纯新手或者一次性项目。
极简字符串拼键(HashMap思路)
特点:不存JSON,存拼接字符串。
SKU表
CREATE TABLE `product_sku` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `product_id` INT UNSIGNED NOT NULL, `spec_key` VARCHAR(100) NOT NULL COMMENT '规格键,color_red|size_xl', `spec_value` VARCHAR(200) NOT NULL COMMENT '规格展示,红色|XL', `price` DECIMAL(10,2) NOT NULL, `stock` INT NOT NULL, UNIQUE KEY `uk_product_spec` (`product_id`, `spec_key`) ) ENGINE=InnoDB;
PHP操作:
// 生成键 $spec_key = 'color_' . $color . '|size_' . $size; // 移除无效字符,做哈希后存储,避免字符串太长 $spec_key_hash = md5($spec_key);
优点:查询快(走索引),不需要JSON解析。 缺点:可读性差,需要额外维护规格值映射。
EAV表结构(最灵活,适合超多规格或自定义属性)
适用于商品属性极多且不固定的场景,但查询复杂度极高,不推荐在常规电商使用。
属性定义表
CREATE TABLE `attribute` ( `attr_id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `attr_name` VARCHAR(50) NOT NULL, -- 颜色、内存、材质等 `is_sku` TINYINT NOT NULL DEFAULT '0' COMMENT '是否属于SKU维度(影响价格库存),还是普通属性' );
SKU 属性关系表
CREATE TABLE `sku_attr_value` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `sku_id` INT UNSIGNED NOT NULL, `attr_id` INT UNSIGNED NOT NULL, `value` VARCHAR(100) NOT NULL, UNIQUE KEY `uk_sku_attr` (`sku_id`, `attr_id`) );
关键设计考虑点
-
JSON vs 字符串:MySQL 5.7+ 支持 JSON 字段,但建议不要在
spec_info字段上直接建普通索引或唯一索引,除非使用虚拟列(Generated Column)。- 更好的做法:添加一个冗余字段
spec_md5,存储md5(json_encode($specs)),并在此字段上建立唯一索引,确保同一商品下不会插入重复的SKU组合。
-- 方案一表的改进版: ALTER TABLE `sku` ADD COLUMN `spec_md5` CHAR(32) NOT NULL; ALTER TABLE `sku` ADD UNIQUE KEY `uk_spu_spec_md5` (`spu_id`, `spec_md5`);
- 更好的做法:添加一个冗余字段
-
库存与价格:
- 不要将库存直接写死为
INT,应预留DECIMAL或INT UNSIGNED,同时最好用乐观锁(版本号)或悲观锁(FOR UPDATE)来防止超卖。 - 价格用
DECIMAL(10,2),避免浮点误差。
- 不要将库存直接写死为
-
图片:多规格商品,每个SKU通常有专属图片,可以在SKU表中加入
sku_image字段;如果所有SKU共用主图,则缺省。 -
数据一致性:在后台编辑商品时,如果规格组合变化,需要确保 SKU表 和 规格表 同步更新(通常使用事务 + 全删重插策略,或者差量更新策略)。
-
前端交互:通常前端会通过
attributes数组请求某个SKU,后端通过唯一索引查询并返回价格和库存。
总结推荐
对于绝大多数PHP电商项目(如ThinkPHP、Laravel框架),建议采用“方案一:SPU + SKU + JSON规格 + MD5唯一索引”。
这是兼顾灵活性、查询性能与开发效率的最佳平衡点,如果是Spring Boot或更多微服务场景,可以考虑用 MongoDB 存储SKU的嵌套文档,但在PHP传统关系型数据库中(MySQL),上述方案最为稳妥。