当前位置: 首页 > 原理解释

数据库原理与应用设计-数据库原理与应用

有时候我会想,数据库这事儿到底该如何写才能显得比较“活”。别总想着堆砌那些标准的开场白,“随着数据库技术的发展..."这种套话听着就有点死板。作为过来人,我认定还不如讲大道理,不如就聊聊那天下午我遇到的一道题,就连吐槽一下当时受刺激的脑子。 那天大约是周五下午三点,实验室里电脑风扇在嗡嗡作响,屏幕上光标闪烁得像个累瘫的小蜜蜂。我正预备写一个带人气的电商系统表结构,突然灵光一闪,想起上周给同学演示时,他们总拿这个表吐槽字段设计得像个砖墙。便我把目光从教科书上移开,盯着屏幕上的 Schema 编辑器,手指头在行里跳来跳去。 一启动我还在犹豫,是不是该把用户信息拆成几十个小字段?毕竟功能如此多,每天录入数据的人多,肯定好办出错。但转念一想,团队里有个老哥,平时做用户管理,就喜爱用“姓名”、“年龄”、“性别”这种一眼就能看明白的字段,哪怕数据量大了点,起码录入个星期都不用翻数据库表。他看着我还在纠结,就笑着问:“兄弟,如此细的表,录入的数据多,我们每天改了几十遍,累不累?
是不是该换个思路?” 我这才意识到,数据库设计的核心不是要把所有信息都塞进一格,而是得让数据能最好地流动起来。便我把“身份证”这俩字段合并成了“用户 ID",然后给 ID 加了个备注字段叫“身份证号”,这样既省空间又不至于让表长到让人想哭的程度。 接着我动手建了表,心里默默想:要是真有人要写一个像学校考勤表那样的表,数据量小但格式固定,那忒好办了;但要是做成一个像电商订单表那样的表,涉及商品、库存、价格、数量、状态、创建工夫,这就有点复杂了。
要是把这些列全按顺序堆上去,那表格长啥样?
是不是得先给每个字段打标签,再给列打标签?我这才明白,数据库设计的第一步实际上是确定“表格宽多少”。 我试着画了两行数据:一行是平年的,一行是跨年的。 表 1:2023 年。用户名:张三;ID:12345; 表 2:2024 年。用户名:李四;ID:67890。 我心想,要是把这两条记录拼起来,是不是就能体现工夫轴的概念? 便我把“工夫”列放了进去。 表 3:2023 年订单。用户名:张三;ID:12345;商品:笔记本电脑;价格:10999.00;数量:1;状态:已支付;创建工夫:2023-09-01 14:30:25。 这一列放上去,数据量就明显大了,从 10 行变成了 50 行。我忍不住吐槽自己:这叫啥?这是要建个 Excel 表格吗? 幸好,我查了 SQL 的文档,找到了“日期”类型。 表 4:2023 年订单。用户名:张三;ID:12345;商品:笔记本电脑;价格:10999.00;数量:1;状态:已支付;创建工夫:2023-09-01 14:30:25。 再建表,感觉数据量又变小了,但又多了个日期。 我深吸一口气,启动纠结命名。是叫“创建工夫”还是“下单工夫”?是“日期型”还是“工夫型”?要是叫“创建工夫”,那数据范围就挺大,可能会涵盖到几百年赶明儿的历史数据。
要是叫“下单工夫”,那就忒局限了。我翻了翻网上的资料,发现大多数系统都用“下单工夫”更合适,出于大多数业务都是围绕交易形成的。 便我把表创建成了:用户表、订单表、订单详情表。 不过,我在建表的时候,加了一个字段叫“订单 ID",自己把原数据里的 ID 都改成 12345,然后按“创建工夫”排序。 接着,我想到了另一个难题:要是我目前要查数据,应当查哪张表? 我写了一行 SQL 语句: SELECT 订单 ID, 商品名称, 价格 FROM 订单表; 这条语句简洁明白,但要是你再想查“还差多少钱还没付”要么“今天第几次下单”这种复杂需求,那肯定得加子查询。 我试着写了一个子查询: SELECT 订单 ID, 商品名称, 价格 FROM ( SELECT 订单 ID, 商品名称, 价格 FROM 订单表 WHERE 状态='已支付' ) AS 好订单; 执行完后,结局集里只有几条“已支付”的订单,其他乱七八糟的订单都被过滤掉了。 那一刻我挺兴奋的,感觉自己的 SQL 水平又提升了一点点。 但随即我又有些迷茫,为啥非得用子查询呢?不如直接加个 WHERE 条件不就行了? 我持续在 IDE 里敲代码,敲出一行: SELECT 订单 ID, 商品名称, 价格 FROM 订单表 WHERE 状态='已支付'; 结局和刚刚一模一样。但这次,要是我要查“已支付”且“商品是手机”的记录,我就得再改: SELECT 订单 ID, 商品名称, 价格 FROM 订单表 WHERE 状态='已支付' AND 商品名称='手机'; 再改一遍呢: SELECT 订单 ID, 商品名称, 价格 FROM 订单表 WHERE 状态='已支付' AND 商品名称='手机' AND 创建工夫=2023-09-01; 这时候我就悟了,数据库查询的本质实际上就是“筛选”和“排序”,而不是非得用子查询这种复杂的语法。 后来我查了表结构,发现表里有个“退款记录”表,里面存着退款信息。 我试着写了一个联合查询(Inner Join): SELECT o.订单 ID, o.商品名称, o.价格 FROM 订单表 o LEFT JOIN 退款记录 r ON o.订单 ID = r.订单 ID WHERE r.状态='已退款'; 执行后,要是订单没退款,那边记录就不会显示出来,但订单表里的数据还在,这就像查网友资料没找到一样,数据是保险的。 想到这儿,我忍不住想:要是我想查“商品名”和“价格”,是不是得把两个表都查一遍再拼起来? 我试着写了一个 Full Outer Join: SELECT o.订单 ID, o.商品名称, o.价格, r.退款金额 FROM 订单表 o LEFT JOIN 退款记录 r ON o.订单 ID = r.订单 ID WHERE o.订单 ID = 12345; 结局出来了:订单 ID 是 12345 的,商品是笔记本电脑,价格是 10999,退款金额是 0。 要是订单退款了,那退款金额就有记录;要是没退款,那退款金额就是 NULL。 最终我遇到了一个真正让人头疼的 SQL:多表连接,还要排序。 我想查“用户”、“订单”和“订单详情”这三张表,看某个用户的订单里买了哪些商品,总共花了多少钱。 那该不该用子查询?我查了维基百科和某个论坛,发现大量人说是,但我也认定有点啰嗦。 便我把这三张表都写成一个大的查询语句: SELECT 用户 ID, 用户名, SUM(订单详情价格) 总花 FROM 用户表 u JOIN 订单表 o ON 用户 ID = 订单用户 ID JOIN 订单详情表 od ON 订单 ID = od 订单 ID GROUP BY 用户 ID, 用户名 HAVING SUM(od 价格) IS NOT NULL ORDER BY 总花 DESC; 执行后,结局挺清楚:按总花降序排列,花最高的是张三,其次是李四。 那一刻我豁然开朗。SQL 别看有点绕,但只要逻辑理顺,用对关联方式,实际上比那些复杂的子查询要直观得多。 后来我意识到,甭管 SQL 写得多么复杂,底层逻辑都是一样的:把数据分给几张表,告诉它们如何联系,最终再按某种规则把结局取出来。 突然我脑补了一个场景:要是我把那个“订单详情表”彻底删了,只剩“订单表”和“用户表”,那我该如何办? 我试着重新建表,持续在 IDE 里敲代码,这次只加了“订单 ID"、“商品名称”、“价格”、“数量”、“总价”几个字段。 建完后,我 наслажда了一行 SQL: SELECT 用户 ID, 用户名, 商品名称, 数量, 总价 FROM 订单表; 此时,我没有损失任何数据,也没有任何富余的字段,只留下了业务最核心的东西。 这让我想到,数据库设计实际上就像搭积木。有些时候你要把积木拆得粉碎,看看能不能用一种新的方式拼出来;有时候你要把一块大积木拆成几块小积木,分别放到不同的桌子上,再想办法把它们连起来。 有一次我拍板给“用户表”加个“备注”字段,原本是想存用户的个人简介。但转念一想,要是用户确实不想公开呢?要是用户是个社恐呢? 便我把“备注”字段删了,改成了“隐私标记”字段:0 代表公开,1 代表隐私。 这样,用户自己能够设置自己的隐私。 我写了一行 SQL: SELECT 用户名, 备注, 隐私标记 FROM 用户表; 查询结局贼友好:要是隐私标记是 0,显示备注信息;要是是 1,则隐藏备注。 那一刻我突然明白,数据库的设计不只是为了存数据,更是为了给数据加个“人格”。 最终,我想问自己一个难题:要是赶明儿毕业了,面试官问我设计一个带图片上传功能的系统表结构,该如何回答? 我想到了之前的经验,也想到到了那个“订单详情表”。 那个表肯定得加张图片字段,那是 jpg 还是 png?是 base64 编码还是直接存路径? 要是是 base64 编码,那数据量就大了,存一张图就要好几百个字节。存路径呢?要是路径乱了,用户扫码看不了图如何办? 我查了资料,发现“存路径”比较保险。 便我把表结构设计成了: `user` 表存 id、username、password、email、privacy_flag; `order` 表存 order_id、order_no、username、order_time、status; `order_item` 表存 order_id、item_name、item_desc、price、quantity、image_url; `user_image` 表存 user_id、image_name、image_url、upload_time。 这种设计,既能保存图片,又不会让订单表变得臃肿。 有时候我认定,最完美的数据库设计,实际上就是一个逻辑闭环。数据如何存,如何查,如何删,都要寻思到未来的变化。 最终我整理了一下思路,拍板明天去翻翻 SQL 的标准查询语法,把刚刚的体验记录下来。
毕竟,作为未来的架构师,不能只凭记忆,还得有理论支撑。 就比如刚刚那个带图片的订单表,要是我要写个查询语句,是不是得用 `SELECT` 加上 `JOIN`? 再比如,要是我要查“所有用户 ID 大于 1000 的用户”,是不是得用 `WHERE username LIKE '%1000%'`? 别看听起来有点离谱,但这就是 SQL 的逻辑。 我给自己倒了一杯咖啡,看着窗外,心想:数据库设计这事儿,实际上挺有意思的,就像是在玩一场没有标准答案的游戏,只要逻辑通顺,就能通关。 自然,我也知道,实际项目中没那么好办。
有时候数据量大了,表都快撑不住了,还得寻思分库分表。但那是另一个故事了。 今天我就把这个设计过程写下来吧,别看有些啰嗦,有些口语化,但希望能帮到正在学习数据库设计的同学。
毕竟,技术这东西,最迷人的地方就在于它让你能创造一个世界,而不只是是记录世界的痕迹。
相关标签:

猜你喜欢

热门阅读

  • 赖柴尔定理-赖柴尔定理
  • 迪拜哪个国家的城市?-迪拜在哪国城市
  • 李毅吧番号及出处-李毅吧番号及出处
  • 贴春联的由来简介50字-春联由来简述
  • 思乡的名言和出处-思乡名言及出处

其他分站