【问题标题】:First-time database design: am I overengineering? [closed]首次数据库设计:我是否过度设计? [关闭]
【发布时间】:2011-01-20 05:00:36
【问题描述】:

背景

我是 CS 一年级的学生,我在我父亲的小企业做兼职。我在现实世界的应用程序开发方面没有任何经验。我用 Python 写过脚本,用 C 写过一些课程,但没有这样的。

我父亲经营一家小型培训公司,目前所有课程都通过外部网络应用程序安排、记录和跟进。有一个导出/“报告”功能,但它非常通用,我们需要特定的报告。我们无权访问实际数据库来运行查询。我被要求建立一个自定义报告系统。

我的想法是创建通用 CSV 导出并将它们导入(可能使用 Python)到每晚托管在办公室的 MySQL 数据库中,我可以从中运行所需的特定查询。我没有数据库方面的经验,但了解非常基础的知识。我已经阅读了一些关于数据库创建和普通表单的内容。

我们可能很快就会开始拥有国际客户,所以我希望数据库在发生这种情况时不会爆炸。我们目前还有几家大公司作为客户,他们拥有不同的部门(例如 ACME 母公司、ACME 医疗保健部门、ACME 身体护理部门)

我想出的架构如下:

  1. 从客户的角度来看:
    • Clients 是主表
    • 客户与他们工作的部门相关联
      • 部门可以分散在一个国家/地区:伦敦的人力资源部、斯旺西的营销部等。
      • 部门与公司的部门相关联
    • 部门与母公司相关联
  2. 从类的角度来看:
    • Sessions 是主表
      • 每节课都有一位教师
      • 为每个会话提供一个 statusid。例如。 0 - 已完成,1 - 已取消
      • 会话被分组为任意大小的“包”
    • 每个包都分配给一个客户

我在一张纸上“设计”(更像是潦草)模式,试图将其规范化为第三种形式。然后我将它插入 MySQL Workbench,这一切对我来说都很漂亮:
(Click here for full-sized graphic)


(来源:maian.org

我将运行的示例查询

  • 哪些还剩学分的客户处于非活动状态(未来没有安排课程的客户)
  • 每个客户/部门/部门的出勤率是多少(由每个会话中的状态 ID 衡量)
  • 一个老师一个月有多少节课
  • 标记出勤率低的客户
  • 人力资源部门的自定义报告及其部门人员的出勤率

问题

  • 这是过度设计还是我的方向正确?
  • 大多数查询需要连接多个表会导致性能大幅下降吗?
  • 我已经向客户端添加了一个“lastsession”列,因为它可能是一个常见的查询。这是个好主意还是我应该严格规范数据库?

感谢您的宝贵时间

【问题讨论】:

  • 亲爱的 CS 一年级学生:请继续使用 StackOverflow。你的问题很有趣,写得很好,很有帮助。换句话说,您在提问者中排名前 1%。
  • 一个部门可以包含其他部门吗?如果是这种情况,则可以使用“has”表将 Division 链接回包含它的 Division。
  • 感谢 cmets 的好意 :) Mark 我将不得不再次查看该项目的文档,但我认为我们没有发现这种情况。感谢您指出。
  • 我不喜欢你的主键命名约定。表 divisions 具有名为 divisionid 的列。你不觉得那是多余的吗?只需将其命名为id。还有你的表名,包括_has_:我会删除它,然后将其命名为例如cities_departments。你的DATETIME 列应该是TIMESTAMP 类型,除非它们是用户输入的值。我认为拥有citiescountries 表是个好主意。您可能会遇到将表限制为单个 status 的问题。考虑使用INT 并对其执行按位比较 - 这样你就可以在那里拥有更多意义
  • @binnyb 关于using id as the name of the primary key 有很多争论,人们在决定之前应该考虑这些。

标签: mysql database database-design schema database-normalization


【解决方案1】:

您的问题的更多答案:

1) 对于第一次解决此类问题的人来说,您几乎是目标。我认为迄今为止其他人关于这个问题的指示几乎涵盖了它。干得好!

2 & 3) 您将受到的性能影响在很大程度上取决于为您的特定查询/过程拥有和优化正确的索引,更重要的是记录量。除非您在主表中谈论超过一百万条记录,否则您似乎正朝着拥有足够主流的设计的方向前进,在合理的硬件上性能不会成为问题。

也就是说,这与您的问题 3 有关,从一开始您可能不应该过分担心性能或对正统化正统的过度敏感。这是您正在构建的报告服务器,而不是基于事务的应用程序后端,它在性能或规范化的重要性方面会有很大不同的配置文件。支持实时注册和调度应用程序的数据库必须注意需要几秒钟才能返回数据的查询。不仅报表服务器功能对复杂和冗长的查询有更大的容忍度,而且提高性能的策略也大不相同。

例如,在基于事务的应用程序环境中,您的性能改进选项可能包括将存储过程和表结构重构到第 n 级,或者为少量常用数据开发缓存策略。在报告环境中,您当然可以这样做,但您可以通过引入快照机制对性能产生更大的影响,其中计划进程运行并存储预配置的报告,并且您的用户访问快照数据而不会对您的数据库层造成压力以每个请求为基础。

所有这些都是冗长的咆哮,以说明鉴于您正在创建的数据库的角色,您采用的设计原则和技巧可能会有所不同。我希望这会有所帮助。

【讨论】:

  • 1.谢谢,放心了! 2 & 3. 我仍然不知道索引是如何工作的,这是我计划阅读的内容。如果我们曾经遇到过达到一百万条记录的“问题”,那么可能会有聘请经验丰富的开发人员的预算:P 感谢您对存在的不同数据库角色的洞察,这对我来说是全新的并且非常有趣。我将研究快照,因为您所描述的基本上是该项目的最终目标。
  • 如果您了解表,那么索引的基础知识非常简单。从概念上讲,索引可以(并且经常)实现为一个表,其中包含很少的列,其内容是从主表复制的,以及对主表的引用,其行经过 keot 排序以便快速访问。 B+Tree 是最常见的索引排列,但索引优化是大玩家拥有差异化技术的地方,所以如果你试图将类比应用得太深,就会变得模糊不清。
【解决方案2】:

你的想法是对的。但是,您可以清理它,并删除一些映射 (has*) 表。

您可以在Departments表中添加CityId和DivisionId。

除此之外,我认为一切都很好......

【讨论】:

  • 如果他想在不同的部门或城市重复使用部门定义,我认为他需要映射表。
  • 是的,我同意.....但听起来一个部门只能在一个城市/部门。如果不是,那么他所拥有的绝对是正确的。
  • 我在办公室写了一篇带有“规范”的 wiki 文章,我将不得不再读一遍,但 Jacob G 是正确的,IIRC 有一些跨部门的部门。 ACME 父母的一个人力资源部门,负责 ACME 医疗保健和 ACME 身体护理。如果我能简化它,我当然会,谢谢你的建议。
【解决方案3】:

我要做的唯一改变是:
1-将您的 VARCHAR 更改为 NVARCHAR,如果您可能要走向国际,您可能需要 unicode。

2- 如果可能,将您的 int id 更改为 GUID(唯一标识符)(这可能只是我个人的偏好)。假设您最终达到了拥有多个环境(dev/test/staging/prod)的地步,您可能希望将数据从一个迁移到另一个。拥有 GUID Id 使这变得更加容易。

3- 公司的三层结构 -> 部门 -> 部门结构可能还不够。现在,这可能是过度设计,但您可以概括该层次结构,以便您可以支持 n 级深度。这将使您的一些查询更加复杂,因此可能不值得进行权衡。此外,任何具有更多层的客户端都可能很容易“填充”到此模型中。

4- 您在客户端表中还有一个状态,它是一个 VARCHAR,并且没有到状态表的链接。我希望能更清楚地了解客户端状态所代表的含义。

【讨论】:

  • 1- 谢谢,我在变音符号和 UTF8 方面遇到了问题,我将为此发布另一个问题。也许这就是问题所在。 2-我在这里阅读了一些关于 SO 的其他问题,对此事有很多相互矛盾的意见,我会在这个主题上做更多的阅读。 3- 我会再次和我父亲讨论这个问题,看看我写的“规范”,看看这是否是我们应该研究的东西。 --继续下一条评论
  • 4- 为简洁起见,我没有在主要问题中讨论它:客户端的状态是它们是活动的(剩余会话)还是非活动的(没有会话剩余)。更清晰,您是指 col 的更具描述性的名称吗?例如。注册状态?感谢您的意见。
  • re #4- 除了你更清晰的名字,如果只有两个状态,活动/非活动,那为什么不把它做成一个位列呢?
  • 不同意 GUID,不寒而栗。它们的性能可能很糟糕。除非您需要复制,否则不要使用它们。
  • 只有当您在表中处理数百万行时,性能才会发挥作用。如果您有这种类型的结构,那么您可以通过顺序指南和创造性索引来缓解这种情况。否则,在打折 GUID 时,“性能”是一个红鲱鱼。
【解决方案4】:

没有。看起来您的设计细节水平很高。

我认为国家和公司在您的设计中实际上是同一个实体,城市和部门也是如此。我会删除国家和城市表(以及 Cities_Has_Departments),并在必要时将布尔标志 IsPublicSector 添加到 Companies 表(或 CompanyType 列,如果有更多选择,而不是简单的 Private Sector / Public Sector)。

另外,我认为您对 Departments 表的使用存在错误。看起来 Departments 表可以作为每个客户部门可以拥有的各种部门的参考。如果是这样,它应该被称为 DepartmentTypes。但是您的客户(我假设他们是与会者)不属于部门类型,它们属于公司中的实际部门实例。就目前而言,您将知道给定客户属于某个地方的人力资源部门,但不知道属于哪个!

换句话说,客户应该链接到您称为 Divisions_Has_Departments 的表(但我将简称为 Departments)。如果是这样,那么如果您想在数据库中使用标准的参照完整性,那么您必须将 Cities 折叠为 Divisions(如上所述)。

【讨论】:

  • 国家/地区表适用于如果/当我们的客户在多个国家/地区开展业务并且每个国家/地区都有不同的人力资源部门时。通过这种方式,我们可以使用我们正在处理的部门所在国家/地区的数据创建报告。对于部门和城市,我认为我们有一个拥有独立人力资源部门的客户。对于他们有主要办事处的两个城市。或者至少这是推理,我会坐下来重新考虑一下,看看它们是否真的有必要。没想到 CompanyType,我会看看是否需要跟踪。
  • RE:depts 表,我最初的想法是将其用作实际部门,部门名称为类型。我没有想到只有部门类型,这似乎更合乎逻辑。关于知道某人属于哪个部门和哪里,我曾认为将部门与城市和部门(与公司相关联)联系起来会奏效。我错了吗?对于将城市分解为部门,一些部门跨越多个城市,我认为甚至可能是国家。我会再调查一下。感谢您的意见。
【解决方案5】:

顺便说一句,值得注意的是,如果您已经在生成 CSV 并希望将它们加载到 mySQL 数据库中,那么 LOAD DATA LOCAL INFILE 是您最好的朋友:http://dev.mysql.com/doc/refman/5.1/en/load-data.html。 Mysqlimport 也值得研究,它是一个命令行工具,基本上是一个很好的加载数据 infile 的包装器。

【讨论】:

    【解决方案6】:

    大部分内容已经说了,但我觉得我可以补充一点:对于年轻的开发人员来说,预先担心性能有点过分是很常见的,而且你关于连接表的问题似乎进入了那个方向。这是一个名为“Premature Optimization”的软件开发反模式。试着从你的脑海中消除这种反射:)

    还有一件事:您认为您真的需要“城市”和“国家”表吗?部门表中的“城市”和“国家”列不足以满足您的用例吗?例如。您的应用程序是否需要按城市和国家/地区列出部门?

    【讨论】:

    • 尽我所能,它不断超越计算 helloworld.c 的大 O,优化当我按照步骤获取 3NF 数据库。我想他们提供的优势是城市/国家名称的连贯性。就像我们在慕尼黑有一个客户,并且出于某种原因,将新学生输入调度系统的人决定将其称为 München 而不是像以前的学生那样将其称为慕尼黑。此外,我们可能需要按城市列出部门,我必须检查一下。谢谢。
    • 在数据库的设计阶段进行优化至关重要!优化并不是为时过早,因为当数据库拥有数百万条记录时,重构起来要困难得多。
    • 我没有说他不应该对他的设计进行压力测试:)
    【解决方案7】:

    根据作为商业智能/报告专家和战略/规划经理的角色,遵循 cmets:

    1. 我同意上述 Larry 的指示。恕我直言,它并没有过度设计,有些东西看起来有点不合适。为简单起见,我会将客户直接标记为公司 ID、部门描述、部门描述、部门类型 ID、部门类型 ID。使用部门类型 ID 和部门类型 ID 作为查找表和内部报告/分析字段的参考,以实现长期一致性。

    2. Packs 表包含“Credit”列,这实际上不应该与 Client 基表相关联,因此如果它们有很多包,您可以看到未来课程还剩下多少学分?应用程序可以处理计算并将其集中存储在 Client 表中。

    3. 公司信息可以使用更多字段,包括明显的地址/电话/等。信息。我还准备长期添加 D&B“DUNs”列(站点/分支/终极),Dun and Bradstreet (D&B) 拥有庞大的公司目录,以后您会发现他们的信息非常有用用于报告/分析。这将解决您提到的多部门问题,并允许您为子/部门/分支/等卷起他们的层次结构。大型军团。

    4. 您没有提到您将使用多少记录,这可能意味着您需要为一项大型开发计划做好准备,使用预打包的“报告”软件可以更快、更轻松地完成这项计划。如果您不处理大型数据库 (

    5. 仅供参考 - 报告洞察:对于大型数据库,您通常有两个数据库实例 a) 用于记录每个详细记录的事务数据库。 b) 位于单独机器上的报告数据库(数据集市/数据仓库)。有关详细信息,请在 Google 上搜索 Star Schema 和 Snowflake Schema。

    问候。

    【讨论】:

    • 1.您的意思是将所有这些列添加到客户端表中吗?我认为这会破坏规范化,并且也很难保持一致,但我不确定我是否理解正确。 2. 包是连续的,只有最近的包才能有信用,所以不需要跟踪多个包。在这种情况下,您仍然建议将其存储在客户端表中吗? 3. 这对搞清楚客户公司的结构很有帮助,我会研究一下,谢谢。
    • 4.我将不得不检查我们预计在明年拥有的客户和会话数量,但对我来说,会话​​表在一年左右达到这么多行似乎是可行的。我会研究报告软件,我没有想到。 5. 好像是我不小心遇到的情况; Web 应用程序将是我们的“交易数据库”,这个项目将是我们的“repoting 数据库”:) 感谢您的意见。
    • 1.是的,将“公司 ID、部门描述、部门描述、部门类型 ID、部门类型 ID”列添加到客户表中。客户属于一家公司、公司内的不同部门类型(IT/Ops/Admin/等)和不同的部门类型(销售/人力资源/营销业务线)。 2. 我只是认为 Credit 与客户或公司相关联,而不是与会话包相关联。这是您可以做出的商业决策。
    • Larry 还提到了合并 Company 和 Country。我完全同意并回到关于 D&B 参考的问题。我会使用 SiteID 或唯一的东西来允许同一公司的多个位置,然后将部门链接到唯一的 SiteID 之一。
    【解决方案8】:

    我只想解决加入多个表会导致性能下降的问题。不要害怕规范化,因为您将不得不进行连接。连接在关系数据库中是正常的和预期的,它们旨在很好地处理它们。您将需要设置 PK/FK 关系(为了数据完整性,这在设计中很重要),但在许多数据库中,FK 不会自动索引。由于它们将在连接中使用,因此您肯定希望从索引 FKS 开始。 PK 通常会在创建时获得索引,因为它们必须是唯一的。确实,数据仓库设计减少了连接的数量,但通常只有在一份报告中需要访问数百万条记录时,才能达到数据仓库的目的。即便如此,几乎所有数据仓库都从事务数据库开始实时收集数据,然后按计划(每晚或每月或任何业务需要)将数据移动到仓库。因此,即使您以后需要设计数据仓库来提高报表性能,这也是一个好的开始。

    我必须说你的设计对于一年级的 CS 学生来说令人印象深刻。

    【讨论】:

      【解决方案9】:

      它没有过度设计,这就是我解决问题的方式。加入很好,不会对性能造成太大影响(除非您将数据库反规范化,否则这是完全必要的,这是不推荐的!)。对于状态,看看您是否可以使用枚举数据类型来优化该表。

      【讨论】:

      • 枚举是邪恶的。每次您需要扩展枚举时,您都必须重建您的表 - 这没关系,直到您的表变得很多 GB。
      • 感谢克里斯的意见和建议,我担心我会创建一个过于复杂的怪物。马丁,状态非常明确和静态:基本上 0-完成课程,1-课程取消,2-没有出现。我认为这三个涵盖了课程的任何可能结果。在这种情况下使用枚举仍然是一个坏主意吗?
      • 在我看来,这对于枚举来说似乎是完美的。所有可能的结果都提前得到满足。 int 也可以,您可以在应用程序中用枚举或静态整数表示。没关系 :) 如果您使用某些工具编辑数据库,枚举会更好看。
      • 当您的大型表必须 24x7 在线并且需要更改枚举时,枚举可能会出现问题(也许邪恶这个词太强了)。鉴于您正在从头开始重新填充表格 - 不要担心。给定一个足够小的数据集,你还不如只使用字符串。
      【解决方案10】:

      我曾在培训/学校领域工作过,我想我会指出,您所谓的“课程”(给定课程的实例)与课程本身之间通常存在 M:1 关系。换句话说,您的目录提供了课程(“西班牙语 101”或其他),但在一个学期中您可能有两个不同的实例(Tu-Th 由 Smith 教授,Wed-Fri 由 Jones 教授)。

      除此之外,这似乎是一个好的开始。我敢打赌,您会发现客户域(通向“客户”的图形)比您建模的更复杂,但在您获得一些真实数据来指导您之前,不要过火。

      【讨论】:

      • 如果我理解正确的话,情况并非如此。 “课程”只是后续会话的组。这不是传统的基于学期的系统。我想不出任何其他可以添加到客户域的东西,你有什么例子吗?我也担心我已经过度复杂了,很高兴不是这样 :) 感谢您的意见。
      【解决方案11】:

      想到了一些事情:

      1. 这些表格似乎适用于报告,但并没有真正运行业务。我认为,当客户注册时,本质上是为参加一系列会议的客户下订单,而该订单可能是针对一家公司的多名员工。看起来“订单”表将真正位于您系统的中心,并推动您的数据捕获和最终报告。 (将您用于开展业务的纸质文档与您的数据库设计进行比较,看看是否存在逻辑匹配。)

      2. 公司通常没有部门。员工有时会改变部门/部门,甚至可能是在会议期间。公司有时会添加/删除/重命名部门/部门。确保表格中可能实时更改的内容不会使后续报告/分组变得困难。由于将如此多的联系人数据拆分到如此多的表格中,您可能必须强制执行非常严格的数据输入验证,以使您的报告保持有意义和包容性。例如,当添加新客户时,确保他的公司/部门/部门/城市与他的同事匹配相同的值。

      3. “包”的概念根本不清楚。

      4. 由于您表示这是一家小型企业,考虑到当前机器的速度和容量,如果性能成为问题,那将是令人惊讶的。

      【讨论】:

        猜你喜欢
        • 2012-08-19
        • 2018-05-02
        • 1970-01-01
        • 2010-09-22
        • 2012-09-23
        • 2011-11-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多