草庐IT

MySQL表设计和规范化协助

coder 2023-10-03 原文

注意:此问题已于 2012 年 11 月 19 日改写以进行澄清。我通常在这里没有太多问题,但在为客户站点设计新产品系统时遇到了困难。我们提供一套产品,每个客户都可以卖给他的客户。我们可能会随时添加新产品,但它们都遵循以下格式:

  1. 类别
  2. 类型
  3. 产品

使用之前的结构举一个真实世界的例子:

  • 棒球装备
    • 手套
      • 罗林斯
      • 耐克
      • 美津浓
    • bat
      • 伊斯顿
      • 路易斯维尔强击手
  • 足球装备
    • 鞋子
      • 耐克
      • 锐步
      • 阿迪达斯
    • 足球
      • 耐克
      • 种苗
      • 威尔逊
  • ....

上面的列表显然还在继续,而且可能要大得多,但它给出了总体思路。

目前,我将特定客户可以销售的产品类型存储在一个平面格式表中,如下所示:

ID  | clientID | categoryID | typeID | productID | customURL
=============================================================
1   |  111     |    1       |   1    |   1       | 1111
2   |  111     |    1       |   2    |   2       | 2222
3   |  111     |    1       |   2    |   3       | 3333
4   |  111     |    2       |   3    |   4       | 4444
5   |  222     |    1       |   1    |   1       | 5555
6   |  222     |    2       |   3    |   4       | 6666
  • 在上面的示例中,类别 1 可以是“棒球装备”,类别 2 可以是“足球装备”
  • 将对应的categoryID、typeID、productID的名称分别存储在3个FK关系的独立表(innodb)中,以保持规范化。
  • 类型指的是二级商品(手套、球棒、鞋子、足球等)。这些数字永远不会相交(这意味着即使一般产品相同,也永远不会有相同的 typeID(棒球鞋与足球鞋有不同的 ID)。
  • 在此表中,clientID 1 可以销售 4 种产品,其中 3 种属于类别 1,1 种属于类别 2。ClientID 2 可以销售 2 种产品,每个类别一种。

我倾向于保持表格的结构,但我知道在其他设计中我可能出于规范化目的将表格分开我不确定是否适用于此。如果我将它们分解,我会看到从一张 table 变成 4 张或更多 table ,如下所示:

productsOffered表

ID  | clientID | productID | customURL
======================================
1   |  111     | 1       | 1111
2   |  111     | 2       | 2222
3   |  111     | 3       | 3333
4   |  111     | 4       | 4444
5   |  222     | 1       | 5555
6   |  222     | 4       | 6666

产品定义表

ID  | productID | typeID | productName
======================================
1   |  1        |    1   | rawlings glove
2   |  2        |    2   | product2
3   |  3        |    2   | product3
4   |  4        |    3   | product4

类型定义表

ID  | typeID | categoryID | typeName
=====================================
1   |  1     |    1       | Gloves
2   |  2     |    1       | Bats
3   |  3     |    2       | Shoes
4   |  4     |    2       | Footballs

类别定义表

ID  | categoryID | catName
=============================
1   |  1         | Baseball Equipment
2   |  2         | Football Equipment

我是不是想多了?这两种方法得到最终解决方案的方式不一样吗(后者只是涉及几个连接来收集平面表,如图 1 所示)?

最佳答案

规范化的目的和好处是它使输入异常数据变得更加困难(理想情况下,不可能)。

例如,在您的图 1 中,如何防止您意外存储 typeid 为 3 和 categoryid 为 1 的行?没什么,除了编写绝对完美的应用程序代码。

但是,如果您使用单表方法,并且必须更改 typeid 3 的父类别,则必须更改一百万个位置的数据以反射(reflect)更改。这意味着在执行清理时锁定表,否则可能会同时插入新数据。

规范化有助于消除冗余存储信息,如果每个离散事实(例如 typeid 3 属于 categoryid 2)只存储一次,那么很容易以原子方式进行更改,并且自动更改对该行的所有引用的含义.

你是对的,需要更多的连接——但前提是你像现在一样在所有地方使用伪键。您不一定需要这样做,您可以改用自然键,并且将使用级联外键声明对它们的引用,因此查找表中的更改也会自动更新引用表。

当然,规范化 的规则并不强制要求使用伪 key 。这些规则只字未提。


关于您的评论:一个伪 key ,或surrogate key , 是用于标识行的“id”列。通常,这些值是通过自动递增机制分配的,该机制可确保唯一性,同时允许并发事务插入行。 id 的值对其标识的行没有意义。


下面显示了您的表在正常形式下的样子,但没有代理键。

productsOffered表

client | product        | customURL
===================================
Smith  | Rawlings Glove | 1111
Smith  | Product 2      | 2222
Smith  | Product 3      | 3333
Smith  | Product 4      | 4444
Jones  | Rawlings Glove | 5555
Jones  | Product 4      | 6666

产品定义表

product        | type
=======================
Rawlings Glove | Gloves
Product 2      | Bats
Product 3      | Bats
Product 4      | Shoes

类型定义表

type      | category
==============================
Gloves    | Baseball Equipment
Bats      | Baseball Equipment
Shoes     | Football Equipment
Footballs | Football Equipment

类别定义表

category
==================
Baseball Equipment
Football Equipment

使用非整数作为主键列的数据类型完全符合关系数据库设计和规范化,因此外键从其他表引用它们。

有充分的理由使用代理键,以提高性能或简洁性或允许其他列中的值自由更改。但是规范化并不强制要求使用代理键。

关于MySQL表设计和规范化协助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13421648/

有关MySQL表设计和规范化协助的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  4. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  5. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  6. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  7. ruby - gem 规范失败 - 2

    我正在为毕业设计开发GEM,TravisCI构建不断失败。这是我在Travis上的链接:https://travis-ci.org/ricardobond/perpetuus/builds/8709218构建错误是:$bundleexecrakerakeaborted!Don'tknowhowtobuildtask'default'/home/travis/.rvm/gems/ruby-1.9.3-p448/bin/ruby_noexec_wrapper:14:in`eval'/home/travis/.rvm/gems/ruby-1.9.3-p448/bin/ruby_noexec_

  8. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

  9. ruby - 如何使用 ruby​​ mysql2 执行事务 - 2

    我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi

  10. ruby-on-rails - 设计通过 reset_password_token 获取用户 - 2

    我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow

随机推荐