本文的核心内容:记录积累一些库表设计方案与技巧

数据库实体与实体间的对应关系

1)数据库表的菜单【分类】设计:如省市关联、图书的一、二级分类。

2)数据库表设计之树形结构的表

3)表的简化方案(特定情况,例如,用户触发过的场景记录表)

4)数据库表设计之购物车,利用Session暂时存储购物车信息。


实体与实体间的对应关系

一对一

一对一,一般用于对主表的补充。假设A表为用户信息表,存储了用户的姓名、性别、年龄等基本信息。用户的家庭住址信息也属于用户的基本信息。我们可以选择将用户的家庭住址信息放到用户信息表,也可以单独建一张表,存储用户的家庭住址信息,以用户信息表的主键作为关联。

需不需要拆分取决:表信息的关联程度、表的字段个数限度。

一对多

一对多,是最常见的一种设计。就是 A 表的一条记录,对应 B 表的多条记录,且 A 的主键作为 B 表的外键。举几个  例子:

  1. 班级表 与 学生表,一个班级对应多个学生,或者多个学生对应一个班级。
  2. 角色表 与 用户表,一个角色对应多个用户,或者多个用户对应一个角色。
    1. 商品表 与 图片表,一个商品对应多张图片,或者多张图片对应一个商品。

多对多

构建一张关系表将两张表进行关联,形成多对多的形式。例如:

  1. 老师表、学生表;一个学生可以选修多个老师的课程、同时一个老师也可以教多个学生。
--教师表CREATE TABLE #Teacher(TeacherId int, Name nvarchar(20));INSERT INTO #Teacher VALUES(1, '张老师'), (2, '王老师');--学生表CREATE TABLE #Student(StudentId int, Name nvarchar(20));INSERT INTO #Student VALUES(1, '小张'), (2, '小赵');--老师学生关系表
CREATE TABLE #Teacher_Student(StudentId int, TeacherId int);INSERT INTO #Teacher_Student VALUES(1, 1), (1, 2),(2, 1), (2, 2);

一:数据库表的菜单【分类】设计:如省市关联、图书的一、二级分类

BookType 一级分类:  少儿、外语、计算机

BookClass 二级分类: 少儿[0-2岁、3-6岁、7-10岁、11-14岁、儿童文学]

外语[英语、日语、韩语、俄语、德语]

计算机[计算机理论、计算机考试、数据库、人工智能、程序设计]

BookInf 图书详情 :    图书信息的详细字段。。。

基于以上关系:我们建表有两种方法

①:建立三张表 一级分类表,二级分类表、图书详情表

一级分类ID->作为二级分类的外键

二级分类ID->作为图书详情的外键

这一种依赖外键,实体模型也比较简单。(不再过多描述)

查询语句:可以采用 left join  on   或者  等值连接  将二级分类的外键与一级分类的主键等值连接即可查询。

②:建立两张表      一级分类和二级分类合并成一张表

图书详情表(引用TypeID为外键)

TypeID        指一级二级分类的ID(唯一标识、主键) 序列自增从1开始。

TypeName  指一级二级分类的名字

ParentID     指二级分类所属一级分类TypeID (若为一级分类则填”0”与二级分类加以区分)

countNumber     指一级图书包含二级图书的个数

二级分类所包含详细图书的个数

数据库查询一级分类信息的SQL

select typeid,typename,parentid,countnumber
from t_booktype   where parentid='0'

数据查询二级分类信息(利用表的自连接)

select  child.typeid,child.typename,
child.parentid,child.countnumber
from t_booktype child ,t_booktype  parent
where child.parentid=parent.typeid

二:MySQL 表中存储树形结构数据

由上面设计技巧引出,如果数据层级有多级呢?简言之就像一棵树一样,我们如何存储树形的数据到数据库。

存储父节点

存储于数据库中,最简单直接的方法,就是存储每个元素的父节点ID,即parent_Id->父节点Id。这种方式方便了插入,但是在某些情况下的查询会束手无策。我们可以增加两个字段(deep,is_leaf)帮助我们更快的查询。

deep=1表示父节点,deep>1 表示子节点。

id
parent_id
deep     //当前树的深度
is_leaf  //是否叶子节点

查询所有父节点deSQL如下:

select * from tree where deep=1

查询某个父节点下的所有子节点:

select * from tree where parent_id="" 

查询某个父节点下的所有后代节点,采用这种库表设计方式,这个需要依靠程序才能实现。

存储路径

将存储根结点到每个节点的路径,这种数据结构,可以一眼就看出子节点的深度。要插入自己,然后查出父节点的Path,并且把自己生成的ID更新到path中去。

如果要查询某个节点下的子节点,只需要根据path的路径去匹配,比如要查询D节点下的所有子节点。

select * from tree where path like '1/4/%'

总结

我建议存储树形结构可以将两者结合起来。

id
parent_id
deep     //当前树的深度
path     //根路径
is_leaf  //是否叶子节点

三:MySQL 表的简化(特定情况,例如:用户触发过的场景记录表)

假设业务中有N多道具,比如用户首次使用某个道具触发特效。根据表里有没有用户相关的道具触发记录来完成判断。

id
user_id //用户Id
item_id //道具ID
flag    //是否触发过特效 0-1
add_time
update_time

毫无疑问,上述表结构是能够满足并实现我们需求的,但是如果有20种道具,那么每个用户最终将有20条数据,数据冗余,如何简化?

解决方案:

定义道具枚举值

public enum ItemOnceFlagEnum {NONE(0),ITEM_ONE(1),//道具1ITEM_TWO(1 << 1), //道具2ITEM_Three(1 << 2), //道具3;private int code;ItemOnceFlagEnum(int code) {this.code = code;}public int getCode() {return code;}public static ItemOnceFlagEnum valueOf(int code) {ItemOnceFlagEnum[] values = ItemOnceFlagEnum.values();for (ItemOnceFlagEnum flag : values) {if (flag.getCode() == code) {return flag;}}return NONE;}
}

建表

id
user_id //用户Id
flag    //是否触发过特效 2的0次幂、2的1次幂。。。
add_time
update_timeuser_id设置为唯一索引

判断是否使用过某道具与添加道具使用记录

    //判断是否使用过某项道具public static boolean isHasThisFlag(long flag, ItemOnceFlagEnum itemOnceFlagEnum) {return (flag & itemOnceFlagEnum.getCode()) > 0;}//添加道具使用记录public Result<Boolean> addOnceFlag(long userId, itemOnceFlagEnum flag) {ItemOnceFlagDO itemOnceFlagDO = itmeOnceFlagService.getOnceFlagMap(Collections.singletonList(userId));long calculateFlag = (Objects.isNull(blockOnceFlagDO) ? 0L : itemOnceFlagDO.getFlag()) | flag.getCode();itemOnceFlag.setFlag(calculateFlag);boolean res = itemOnceFlagService.addOrUpdateOnceFlag(itemOnceFlag);}

添加记录SQL

"INSERT INTO xxxx ( ) VALUES ( :1.userId,  :1.flag ) " +" ON DUPLICATE KEY UPDATE flag = :1.flag "

某一项道具为具体2次幂值(仅能够维护2种状态,有或无),flag代表所有触发道具2次幂和。

通过 (flag & itemOnceFlagEnum.getCode())>0  判断是否有某项道具

通过 flag | temOnceFlagEnum.getCode();  添加道具记录


四:购物车模块的库表设计

在电商软件,必不可少的模块就是购物车。

我分享两种设计方法:

①:维护一张购物车表,以用户ID为外键

一个用户一个购物车,用户注册成功的同时,为用户在购物车表内维护一个专属于用户的购物车。(根据我以前学到的知识,这一步可以为用户表创建Insert触发法器,当用户注册成功[触发器将用户ID作为外键插入购物车表],用户即拥有了唯一的购物车)

T_Car

字段

类型

说明

Car_ID

Varchar2(36)

购物车编号  主键

User_ID

Varchar2(36)

外键  用户唯一标识

Car_Status

Varchar2(4)

购物车状态

T_Shop_Item

字段

类型

说明

Shop_item_ID

Varchar2(36)

购物项编号  主键

Car_ID

Varchar2(36)

购物车编号 外键

Product_ID

Varchar2(36)

商品编号   外键

Count

Number(4)

数量

Price

number(8,2)

价格

ProductName

Varchar(30)

商品名

这么实现购物车的弊端:①:非该网站的注册用户无法将商品加入购物车。这与现实的情况不符合。一般我们访问某宝,某东,我们可以以游客的方式将商品加入购物车,直到下订单、付款时才要求我们必须登录。②:每个用户维护一下购物车似乎不太明智,顾客将商品加入购物车到下订单,完成交易,这一需求对数据库更改频繁。

②:所有用户共用一个”购物车”

我们可以直接以用户ID为标识,区分购物车商品所属的用户。

T_Shop_Item

字段

类型

说明

Shop_item_ID

Varchar2(36)

购物项编号  主键

User_ID

Varchar2(36)

用户编号 外键

ProductID

Varchar2(36)

商品编号   外键

Count

Number(4)

数量

Price

number(8,2)

价格

ProductName

Varchar(30)

商品名

即使减少了一张购物车表,但是这表还需要用户登录才能记录用户添加商品情况。

③:利用Session暂时存储购物车内的东西

[用户不登录就能添加商品到购物车;用户登录状态将Session中的信息存入非关系型数据库、关系型数据库。将购物车内的东西持久化存储]

明确一点:Session -> 一次会话有效(在用户不关闭浏览器的前提下,默认存在30分钟;用户关闭浏览器再次打开后,未登录用户的购物车将清空。)

解决如何用Session存储购物车内信息。

第一个难点:那么如何准确区分不同的商品? (自然是商品ID)

第二个难点:那么如何准确标记一个进入购物车的商品? (只有商品ID是不行的)

商品ID,通过商品ID,我们可以查询到商品详情。(价格、名称等等)

同一商品的购买数量。这一信息是不存在于商品详情的。【重点,不能忽略】

这两个信息必须存储。于是我想到Map(两个原因)。

①:存储两个值

Map<String,Integer> map=new HashMap<String,Integer>();

key存储 商品ID,  value存储 商品数量(购买数量)

②:保证商品ID(键)的唯一性

Map的特点:键唯一,值可以重复。我们以商品ID为键,这样就可以保证商品的唯一性。

我们再将map存入Session中就可以了。

当用户添加商品时,只需先从Session中取出map,迭代遍历判断key是否已经存在,若存在取value值加1;若不存在则将商品ID作为key,value数量默认为1,Put进map。

当用户(未登录)查看购物车时,只需从Session中取商品ID和数量,就可以显示购车内商品的详细信息,计算购物车内的商品总价格。

这种存储方式简化了添加商品进入购物车和删除购物车里商品的操作。但是却不得不再次封装一个Map对象将购物车详情页面的信息存储进去,以供购物车展示页面显示数据。

(这是我第一次考虑的存储方案,写到查看购物车详情页面才发现不合理之处。)

改进版本:

Map <String,Goods> map=new HashMap<String,Goods> ();

Key ->商品id

Value->购物车页面需要展示的商品详情(商品名、商品ID、商品数量、商品价格等)

核心:value值维护不再是一个商品数量,而是我们封装的模拟购物车实体对象

JSP页面的效果图

源代码:添加商品的Action

 //添加商品Actionpublic String addToCar() throws Exception{//尝试从Session取出购物车ValueStack vs=ActionContext.getContext().getValueStack();Map<String,Goods> car=(Map<String,Goods>)vs.findValue("#session.car");BookService service=new BookServiceImpl();if(car==null){   //判断购物车是否存在//不存在则创建,将用户添加的商品加入Map 存入SessionMap<String,Goods> map=new HashMap<String,Goods>();//查询数据将商品详情查询出出来,把我们感兴趣的属性封装到Goods实体中Book book=service.getSingleBook(bookid);Goods goods=new Goods();goods.setBookid(book.getBookid());goods.setCount(1);goods.setPrice(book.getPrice());goods.setCurrentprice(book.getCurrentprice());goods.setProductname(book.getProductname());map.put(bookid, goods);vs.setValue("#session.car", map);}else{      //判断用户添加的商品是否已经存在于购物车里//若存在根据key取出Goods->修改商品选购的数量//不存在则是首次添加,数量默认为1 ,将商品Put入mapif(car.containsKey(bookid)){Goods product=car.get(bookid);product.setCount(product.getCount()+1);}else{          Book book=service.getSingleBook(bookid);Goods goods=new Goods();goods.setBookid(book.getBookid());goods.setCount(1);goods.setPrice(book.getPrice());goods.setCurrentprice(book.getCurrentprice());goods.setProductname(book.getProductname());                car.put(bookid, goods);}}   return Action.SUCCESS;}

数据库-库表设计 【分享一些库表设计经验】相关推荐

  1. 中文网页设计分享之《banner设计篇》

    很多同学都觉得做banner无非就是摆几张图,打几个字的事,一点技术含量都没有,像很多门户网站的banner基本上都是编辑做的.我觉得没那 么简单,其实一个banner可以研究的东西还是很多的,而且做 ...

  2. mysql数据库中到底能建多少张表?(单实例下单个库)

    单实例mysql数据库中到底能建多少张表? 业务两个同学今天就这个问题过来探讨,他们的诉求是: 1. 一个用户的表要全部放到一个数据库中. 2. 预计1000个用户每个用户初步规划1000张表. 这1 ...

  3. 数据库(3)——MySQL建库、建表

    这里将自己学习数据库的一些笔记.积累分享一下,如果涉及到了文章.文字侵权,请联系我删除或调整. 一.MySQL建库.建表 1.1 查看数据库.查看数据表 查看mysql服务器中所有数据库 show d ...

  4. 分布式专题-数据库分库分表之Mycat01-数据库性能优化方案

    目录导航 前言 为什么要分库分表 数据库性能瓶颈主要原因 数据性能优化方案 SQL 与索引 表与存储引擎 架构 配置 操作系统与硬件 架构演进与分库分表 单应用单数据库 多应用单数据库 多应用独立数据 ...

  5. mysql数据库入门教程(7):库和表的管理

    DDL语言 #DDL /* 数据定义语言 库和表的管理 一.库的管理 创建.修改.删除 二.表的管理 创建.修改.删除 创建: create 修改: alter 删除: drop */ 一.库的管理 ...

  6. 在名为商品库的数据库中包含有商品规格表Content和商品特性表Property

    在名为商品库的数据库中包含有商品规格表Content和商品特性表Property,它们的定义分别为: Content(Code  Char(10),Class  Char(8),Price Float ...

  7. 数据库基础--数据库基础管理(创建库/表 以及插入修改删除表数据)

    1.啥子是数据库–存储数据的仓库 2.什么是数据–音乐,电影,文本,图片等 常见的数据库软件 Oracle 不开源 跨平台 厂商:甲骨文 MySQL 开源 跨平台 厂商:甲骨文 SQL Server ...

  8. Oracle数据库查看表空间sql语句、查看Oracle数据库表空间剩余 、修改表空间、库备份

    一  Oracle数据库查看表空间sql语句 1.oracle查看表空间当前用户 SQL>  select  username,default_tablespace  from user_use ...

  9. mysql数据表案例_mysql中库和表的简单操作案例

    mysql中库和表的简单操作案例 发布时间:2020-12-05 09:54:06 来源:亿速云 阅读:71 作者:小新 这篇文章主要介绍mysql中库和表的简单操作案例,文中介绍的非常详细,具有一定 ...

  10. Mysql库及表的基本概念、增删查改操作以及表的约束、多表联查

    什么是数据库? 存储数据的仓库 数据库的存储介质:磁盘和内存. 为什么要用数据库不用文件?(数据库与文件进行数据存储的区别) 1.文件存储安全性低 2.数据库对数据进行良好的存储,查询以及管理 3.便 ...

最新文章

  1. DSGN:基于深度立体几何网络的3D目标检测(香港大学提出)
  2. spark官方文档_Apache Spark 文档传送门
  3. union和union all有什么区别
  4. react 子组件ref_React中Ref 的使用方法详解
  5. 《Java 核心技术卷1 第10版》学习笔记------ 参数数量可变的方法
  6. 如何用C#检查硬盘是否是固态硬盘SSD
  7. python程序设计题答案_《Python程序设计》习题与答案
  8. JavaSE——Java基础语法(二进制、变量、方法(函数)、运算符、转义字符)
  9. OXite 微软一款基于asp.net mvc架构的blog内容管理系统
  10. JS使用技巧2——momentjs太重了吗?试试dayjs和miment吧
  11. java list 区别_Java中List和ArrayList的区别
  12. Flask程序的基本结构
  13. linux cp 复制目录下文件到另一个目录下
  14. 前端实现序列帧_最简单的序列帧动画canvas插件
  15. 写在《大国崛起》之后,“中国崛起”之前(二)
  16. java视频生成缩略图_Java调用ffmpeg工具生成视频缩略图实例
  17. 游戏开场镜头拉近(Unity3D开发之四)
  18. 2018.8 安徽师大附中培训游记
  19. 折纸效果! Cocos Creator 3.0
  20. 小孩用的台灯什么样的品牌好?2023儿童台灯灯具品牌排行榜

热门文章

  1. oracle添加redo,添加redolog组成员
  2. chromium 浏览器多进程架构小科普
  3. windows 两台电脑通过移动热点传输文件
  4. Coreset-Based Neural Network Compression简记
  5. 关于4418开发和6818开发
  6. 十年前的生活小技巧文章
  7. 搭建nfs调试arm开发板
  8. 16.【linux驱动】spi驱动TFT液晶屏
  9. openvswitch console输出
  10. 别人都不知道的“好用”网站,让你的效率飞快