电商网站,商品规格的设计思路

1.商品规格数据结构

如图,这是一个商品详情页,它包括商品名称、价格、描述、规格等等数据信息。我们应该如何把这些数据存储到数据库,如何设计数据模型,是一个很关键的问题。如果设计的不好,会大大增加开发、运营、维护的成本。

首先,我们需要知道,这些参数不能存在一张表里,因为 每个商品 的规格参数不同,一个商品的每个规格的价格、名字等参数都不同。一个商品和该商品的所有规格是一对多的关系,所以要分成多个表存储。

1.1.SPU和SKU

为了更准确描述商品及细分差别,抽象出两个概念:SPU和SKU,了解一下:

SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集

就比如说是,上图的Iphone11,就是一个spu(它不是一个商品,而是一组),是一组有共同属性的商品,它们的共同属性就是,它们都叫Iphone11。(比如,Iphone11有金色、银色、有256G、64G。所以Iphone是一组商品。)

SPU是一个抽象的商品集概念,为了方便后台的管理。

SKU:Stock Keeping Unit(库存量单位),SPU商品集因具体特性不同而细分的每个商品

SKU才是具体要销售的商品,每一个SKU的价格、库存可能会不一样,用户购买的是SKU而不是SPU

比如说,绿色的、64G、优惠套装1的Iphone11就是一个sku。把所有规格参数,排列组合组成了一个商品集spu。一个sku才是一个具体的商品,仔细数数还是很多的。

所以不要以为1个商品存起来很简单。

1.2.数据库设计分析

1.2.1.思考并发现问题

弄清楚了SPU和SKU的概念区分,接下来我们一起思考一下该如何设计数据库表。

首先来看SPU,SPU的表应该有哪些字段来描述?

id:主键
title:标题
description:描述
specification:规格
packaging_list:包装
after_service:售后服务
comment:评价
category_id:商品分类
brand_id:品牌

spu里都是某个商品集里所有商品的相同点,比如名称都叫Iphone11、商品分类、品牌也都一样。

 

再看下SKU应该有的数据库字段 

id:主键
spu_id:关联的spu
price:价格
images:图片
stock:库存
颜色?
内存?
硬盘?

除了上面的所有商品都有的规格参数(如,id,spu_id,price等),不同的商品种类还会有它独特的参数,比如,手机有内存大小、衣服有尺码大小等等,这些独特的规格参数该如何解决?

其实颜色、内存、硬盘属性都是规格参数中的字段。所以,要解决这个问题,首先要能清楚规格参数。

1.2.2.分析规格参数

仔细查看每一种商品的规格你会发现:

虽然商品规格千变万化,但是同一类商品(如手机)的规格是统一的,比如说,所有手机都有 品牌、内存、型号、长度、宽度、重量、操作系统等等参数。在搜索商品时,通常也会有按商品种类搜索的功能。

比如这种搜索。

1.2.3.SKU的特有属性

SKU中会有一些特殊属性,用来区分不同的SKU,我们称为SKU特有属性。如Iphone11的颜色、内存属性。

不同种类的商品,一个手机,一个衣服,其SKU属性不相同

同一种类的商品,比如都是衣服,SKU属性基本是一样的,都是颜色、尺码等。

这样说起来,似乎SKU的特有属性也是与分类相关的?事实上,仔细观察你会发现,SKU的特有属性是商品规格参数的一部分

也就是说,我们没必要单独对SKU的特有属性进行设计,它可以看做是规格参数中的一部分。这样规格参数中的属性可以标记成两部分:

  • spu下所有sku共享的规格属性(称为全局属性)
  • 每个sku不同的规格属性(称为特有属性)

1.2.4.搜索属性

过滤条件中的屏幕尺寸、运行内存、网路、机身内存、电池容量、CPU核数等,在规格参数中都能找到 。

也就是说,规格参数中的数据,将来会有一部分作为搜索条件来使用。我们可以在设计时,将这部分属性标记出来,将来做搜索的时候,作为过滤条件。要注意的是,无论是SPU的全局属性,还是SKU的特有属性,都有可能作为搜索过滤条件的,并不冲突,而是有一个交集:

1.3.规格参数表

1.3.1.表结构

规格参数的格式:

可以看到规格参数是分组的,每一组都有多个参数键值对。不过对于规格参数的模板而言,其值现在是不确定的,不同的商品值肯定不同,模板中只要保存组信息、组内参数信息即可。

因此我们设计了两张表:

  • tb_spec_group:组,与商品分类关联
  • tb_spec_param:参数名,与组关联,一对多

1.3.2.规格组

规格参数分组表:tb_spec_group

规格组有3个字段:

  • id:主键
  • cid:商品分类id,一个分类下有多个模板
  • name:该规格组的名称。

1.3.2.规格参数

规格参数表:tb_spec_param

按道理来说,我们的规格参数就只需要记录参数名、组id、商品分类id即可。但是这里却多出了很多字段,为什么?

还记得我们之前的分析吧,规格参数中有一部分是 SKU的通用属性,一部分是SKU的特有属性,而且其中会有一些将来用作搜索过滤,这些信息都需要标记出来。

1.通用属性

generic来标记是否为通用属性:

true:代表通用属性

false:代表sku特有属性

 

2.搜索过滤

与搜索相关的有两个字段:

searching:标记是否用作过滤

true:用于过滤搜索

false:不用于过滤

segments:某些数值类型的参数,在搜索时需要按区间划分,这里提前确定好划分区间比如电池容量,0~2000mAh,2000mAh~3000mAh,3000mAh~4000mAh

3.数值类型

某些规格参数可能为数值类型,这样的数据才需要划分区间,我们有两个字段来描述:

numberic:是否为数值类型

true:数值类型

false:不是数值类型

unit:参数的单位

3.SPU和SKU数据结构

规格确定以后,就可以添加商品了,先看下SPU和SKU的数据库表

3.1.SPU表

CREATE TABLE `tb_spu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'spu id',
  `title` varchar(255) NOT NULL DEFAULT '' COMMENT '标题',
  `sub_title` varchar(255) DEFAULT '' COMMENT '子标题',
  `cid1` bigint(20) NOT NULL COMMENT '1级类目id',
  `cid2` bigint(20) NOT NULL COMMENT '2级类目id',
  `cid3` bigint(20) NOT NULL COMMENT '3级类目id',
  `brand_id` bigint(20) NOT NULL COMMENT '商品所属品牌id',
  `saleable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否上架,0下架,1上架',
  `valid` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0已删除,1有效',
  `create_time` datetime DEFAULT NULL COMMENT '添加时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=208 DEFAULT CHARSET=utf8 COMMENT='spu表,该表描述的是一个抽象的商品,比如 iphone8';

与我们前面分析的基本类似,但是似乎少了一些字段,比如商品描述。

我们做了表的垂直拆分,将SPU的详情放到了另一张表:tb_spu_detail

CREATE TABLE `tb_spu_detail` (
  `spu_id` bigint(20) NOT NULL,
  `description` text COMMENT '商品描述信息',
  `generic_spec` varchar(10000) NOT NULL DEFAULT '' COMMENT '通用规格参数数据',
  `special_spec` varchar(1000) NOT NULL COMMENT '特有规格参数及可选值信息,json格式',
  `packing_list` varchar(3000) DEFAULT '' COMMENT '包装清单',
  `after_service` varchar(3000) DEFAULT '' COMMENT '售后服务',
  PRIMARY KEY (`spu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

这张表中的数据都比较大,为了不影响主表的查询效率我们拆分出这张表。

需要注意的是这两个字段:generic_spec和special_spec

规格参数商品分类绑定,一个分类下的所有SPU具有类似的规格参数。SPU下的SKU可能会有不同的规格参数信息,因此我们计划是这样:

SPUDetail中保存通用的规格参数信息。

SKU中保存特有规格参数。

来看下我们的表如何存储这些信息。

3.1.1.generic_spec字段

首先是generic_spec,其中保存通用规格参数信息的值,这里为了方便查询,使用了json格式:

json结构,其中都是键值对:

key:对应的规格参数的spec_param的id

value:对应规格参数的值

3.1.2.special_spec字段

我们说spu中只保存通用规格参数,那么为什么有多出了一个special_spec字段呢?

以手机为例,品牌、操作系统等肯定是全局通用属性,内存、颜色等肯定是特有属性。

当你确定了一个SPU,比如小米的:红米4X

全局属性值都是固定的了:

品牌:小米
型号:红米4X

特有属性举例:

颜色:[香槟金, 樱花粉, 磨砂黑]
内存:[2G, 3G]
机身存储:[16GB, 32GB]

颜色、内存、机身存储,作为SKU特有属性,key虽然一样,但是SPU下的每一个SKU,其值都不一样,所以值会有很多,形成数组。

我们在SPU中,会把特有属性的所有值都记录下来,形成一个数组。

里面又有哪些内容呢?

来看数据格式:

也是json结构:

key:规格参数id

value:spu属性的数组

那么问题来:特有规格参数应该在sku中记录才对,为什么在spu中也要记录一份?

因为我们有时候需要把所有规格参数都查询出来,而不是只查询1个sku的属性。比如,商品详情页展示可选的规格参数时:

 

刚好符合我们的结构,这样页面渲染就非常方便了。

3.2.SKU表

CREATE TABLE `tb_sku` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'sku id',
  `spu_id` bigint(20) NOT NULL COMMENT 'spu id',
  `title` varchar(255) NOT NULL COMMENT '商品标题',
  `images` varchar(1000) DEFAULT '' COMMENT '商品的图片,多个图片以‘,’分割',
  `price` bigint(15) NOT NULL DEFAULT '0' COMMENT '销售价格,单位为分',
  `indexes` varchar(100) COMMENT '特有规格属性在spu属性模板中的对应下标组合',
  `own_spec` varchar(1000) COMMENT 'sku的特有规格参数,json格式',
  `enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0无效,1有效',
  `create_time` datetime NOT NULL COMMENT '添加时间',
  `last_update_time` datetime NOT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`id`),
  KEY `key_spu_id` (`spu_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='sku表,该表表示具体的商品实体,如黑色的64GB的iphone 8';

还有一张表,代表库存:

CREATE TABLE `tb_stock` (
  `sku_id` bigint(20) NOT NULL COMMENT '库存对应的商品sku id',
  `seckill_stock` int(9) DEFAULT '0' COMMENT '可秒杀库存',
  `seckill_total` int(9) DEFAULT '0' COMMENT '秒杀总数量',
  `stock` int(9) NOT NULL COMMENT '库存数量',
  PRIMARY KEY (`sku_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='库存表,代表库存,秒杀库存等信息';

问题:为什么要将库存独立一张表?

因为库存字段写频率较高,而SKU的其它字段以读为主,因此我们将两张表分离,读写不会干扰。

特别需要注意的是sku表中的indexes字段和own_spec字段。sku中应该保存特有规格参数的值,就在这两个字段中。

3.2.1.indexes字段

在SPU表中,已经对特有规格参数及可选项进行了保存,结构如下:

{
    "4": [
        "香槟金",
        "樱花粉",
        "磨砂黑"
    ],
    "12": [
        "2GB",
        "3GB"
    ],
    "13": [
        "16GB",
        "32GB"
    ]
}

这些特有属性如果排列组合,会产生12个不同的SKU,而不同的SKU,其属性就是上面备选项中的一个。

比如:

红米4X,香槟金,2GB内存,16GB存储

红米4X,磨砂黑,2GB内存,32GB存储

你会发现,每一个属性值,对应于SPUoptions数组的一个选项,如果我们记录下角标,就是这样:

红米4X,0,0,0

红米4X,2,0,1

既然如此,我们是不是可以将不同角标串联起来,作为SPU下不同SKU的标示。这就是我们的indexes字段。

这个设计在商品详情页会特别有用:

当用户点击选中一个特有属性,你就能根据 角标快速定位到sku。

3.2.2.own_spec字段

看结构:

 

{"4":"香槟金","12":"2GB","13":"16GB"}

保存的是特有属性的键值对。

SPU中保存的是可选项,但不确定具体的值,而SKU中的保存的就是具体的值。

 

4.整体表结构

整体的表结构非常复杂。最好分开来看,平时开发时,也都是开发某个模块,也就涉及其中的几张表,只要把这几张表的关系捋清楚就行了。

1.品牌和spu的关系是1对多,一个品牌有多个spu,比如说品牌苹果,下面有IPhone6、Iphone7。

2.分类和spu也是1对多,比如,分类手机,下面有Iphone6、Iphone7。搜索时,需要直接从分类查找该分类下的spu。

3.spu和sku的关系是1对多,比如,Iphone6下有Iphone6(银色,16G)。

 

4.规格组(spec_group)合规格参数(spec_param)的关系是1对多,比如规格组的主体对应参数 品牌、型号。规格组的 基本信息 对应 参数 机身颜色、机身重量(g)、机身材质工艺

5.分类(category)和参数组是1对多。分类和规格参数也是一对多,之所以分类和规格参数、规格参数组关联,就是为了搜索的时候用到。用户选中一个分类(比如说:手机),然后根据手机这个分类的参数组和规格参数搜索到合适的手机。规格参数组里的searching字段就是判断,该参数是不是用于搜索的。

 

6.规格参数表里还有一个generic字段,判断该字段是否是通用字段,如果是,那就是该spu下所有sku都相同的参数。该参数就会以特定json格式(格式就是  规格参数id:唯一的值)存在spu_detail里;如果不是通用字段,会以json格式(格式就是  规格参数id:所有可能的值的数组)存储在spu_detail表里。

 

7.sku表里的own_spec表存的是该sku所有特有参数的键值对,indexs存的是它所有特定参数的值在spu_detail表里special_param字段的json数组的下表。

4.1表展示

1.spec_group 规格参数组 表

2.spec_param 规格参数 表

3.sku表

4.spu表

5.spu_detail表

  • 25
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值