数据库设计之商品表分析2

在上一期,规格确定以后,就可以添加商品了,先看下数据库表

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 '商品描述信息',
  `specifications` varchar(3000) NOT NULL DEFAULT '' COMMENT '全部规格参数数据',
  `spec_template` varchar(1000) NOT NULL COMMENT '特有规格参数及可选值信息,json格式',
  `packing_list` varchar(1000) DEFAULT '' COMMENT '包装清单',
  `after_service` varchar(1000) DEFAULT '' COMMENT '售后服务',
  PRIMARY KEY (`spu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

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

需要注意的是这两个字段:specifications和spec_template。

1.1 SPU中的规格参数

SPU的规格参数也就是specification和spec_template这两个字段里存放的json字符串。
当你确定了一个SPU,比如小米的:红米4X

specification全局属性举例:

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

spec_template特有属性举例:

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

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

展开specification

[{
	"group": "主体",
	"params": [{
		"k": "品牌",
		"searchable": false,
		"global": true,
		"v": "小米(MI)"
	}, {
		"k": "型号",
		"searchable": false,
		"global": true,
		"v": "小米6"
	}, {
		"k": "上市年份",
		"searchable": false,
		"global": true,
		"numerical": true,
		"unit": "年",
		"v": 2017.0
	}]
}, {
	"group": "基本信息",
	"params": [{
		"k": "机身颜色",
		"searchable": false,
		"global": false,
		"options": ["亮蓝色", "亮黑色", "陶瓷黑"]
	}, {
		"k": "机身重量(g)",
		"searchable": false,
		"global": true,
		"numerical": true,
		"unit": "g",
		"v": 168
	}, {
		"k": "机身材质工艺",
		"searchable": true,
		"global": true,
		"v": null
	}]
}, {
	"group": "存储",
	"params": [{
		"k": "内存",
		"searchable": true,
		"global": false,
		"numerical": false,
		"unit": "GB",
		"options": ["4GB", "6GB"]
	}, {
		"k": "机身存储",
		"searchable": true,
		"global": false,
		"numerical": false,
		"unit": "GB",
		"options": ["64GB", "128GB"]
	}]
}]
	...

可以看到,与规格参数表中的模板相比,最大的区别就是,这里指定了具体的值,因为商品确定了,其参数值肯定也确定了。

特有属性用举例存储。

我们发现特有属性也是有的,但是,注意看这里是不确定具体值的,因为特有属性只有在SKU中才能确定。这里只是保存了options,所有SKU属性的可选项。

在哪里会用到这个字段的值呢,商品详情页的规格参数信息中:

展开spec_template字段

{
	"机身颜色": ["亮蓝色", "亮黑色", "陶瓷黑"],
	"内存": ["4GB", "6GB"],
	"机身存储": ["64GB", "128GB"]
}

可以看出,里面只保存了规格参数中的特有属性,而且格式进行了大大的简化,只有属性的key,和待选项。

为什么要冗余保存一份?

因为很多场景下我们只需要查询特有规格属性,如果放在一起,每次查询再去分离比较麻烦。

比如,商品详情页展示可选的规格参数时:
在这里插入图片描述

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格式,反序列化时应使用linkedHashMap,保证有序',
  `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中应该保存特有规格参数的值,就在这两个字段中。

2.1SKU中特有规格参数
2.1.1 indexes字段

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

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

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

比如:

  • 红米4X,香槟金,2GB内存,16GB存储
  • 红米4X,磨砂黑,2GB内存,32GB存储

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

  • 红米4X,0,0,0
  • 红米4X,2,0,1

既然如此,我们是不是可以将不同角标串联起来,作为SPU下不同SKU的标示。这就是我们的indexes字段。
在这里插入图片描述
这个设计在商品详情页会特别有用:
在这里插入图片描述
当用户点击选中一个特有属性,你就能根据 角标快速定位到sku。

2.1.2 own_spec字段

看结构:

{"机身颜色":"香槟金","内存":"2GB","机身存储":"16GB"}

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

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

这样,在页面展示规格参数信息时,就可以根据key来获取值,用于显示。

3.总结

数据库设计要从显示物理中抽象物体的属性,将相同的属性抽离,创建简单,便于查询的表结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值