【问题标题】:compound FK table of many to many relationship database多对多关系数据库的复合外键表
【发布时间】:2018-06-10 22:26:21
【问题描述】:

我正在构建一个 api,但我太害怕 db 设计出错了。我正在尝试练习一个地址簿,员工可以在其中拥有他们的地址(家庭、工作、其他)。那么这是多对多的关系吗?

我的数据库设计正确吗?为了灵活性而创建了一个复合表

ON DELETE 和 ON UPDATE 在这里重要吗?如何设置它以便删除员工,我们不想在其他 2 个表中保留其他记录?

【问题讨论】:

  • address_type (FK address_type_id with person_address), person_address (FK person_id with person, CASCADE) - no?

标签: php mysql sql database-design data-modeling


【解决方案1】:

首先,我不得不补充说,SO 并不是真正适合这个地方,我不确定,但如果有一个仅用于数据库的站点/板,我不会感到惊讶。很多这些东西是个人喜好和意见。

可能这样的地方会更合适:

https://dba.stackexchange.com/

也就是说:

  • 我会将表中的 PK 更改为 id,因此 address_type.id 将只是 id 并且对于 person 是相同的。做 person.person_id 就变得多余了
  • id 应该是INT(10) unsigned AUTO INCREMENT 10 是十位,或大约 99,999,999,999。您不能有负 ID,因此数据库应该强制执行此操作。我做 10 是因为它是 INT(11) 并且保留了标志位置。这不是真的必要,但我对任何 unsigned int 都是出于习惯。
  • 我会复数桥表persons_addresses。因为,personaddress 中的记录是针对一个实体的。桥表中的记录用于多个实体。对我来说,它更容易分辨出它是一张桥牌桌。例如,所有其他都是单数,这些都是复数。

“命名约定”的主要内容是保持一致。如果你为你的 ID 做{table}_id,那么就这样做。如果您使用person,请不要为表格执行zipcodes 之类的操作。甚至列名,如果你做person_id,那么不要做任何列,如FullNamefullNameFull_name等。我会说选择一种方法并坚持下去,它会让你在编写代码时更容易如果您提前知道表名将是单数。正如我所说,我喜欢桥牌桌的复数用法,因为您很少单独使用它们。

为了关系。您仍然需要分别删除 personaddress。但是如果您将它们更改为级联,persons_addresses 中的记录将被更新或删除。我是这样想的:定义关系的表是接收更改的表。

这是应该的方式。假设您有 2 个具有相同地址的人员记录。如果您删除一个人,您不希望从他们两个人中删除该地址。此外,如果某人的地址被删除,您可能不希望将其删除。所以最多应该是:

person > persons_addresses > address

我不确定当桥表中没有记录时是否有自动删除地址的方法。我一直只是手动完成,但如果没有更好的方法,您可以使用 trigger 来完成。

供参考:

触发器是与表相关联的命名数据库对象,并在表发生特定事件时激活。 https://dev.mysql.com/doc/refman/5.7/en/triggers.html

老实说,我从来没有这样做过,而且我认为触发器可能不会在级联操作上触发,我记得一些关于仅在 SQL 语句上触发的事情。在这种情况下,最好只使用触发器从person 中删除。因此,您将删除一个人,触发器将触发,您将检查是否有其他人使用该地址,如果false 您删除了persons_addresses 记录和address 记录。如果true 你只会删除persons_addresses 记录。


我会做的另一件事是将地址分解为单独的zipcode。在我的工作中,我们购买了一个包含所有美国邮政编码的 DB 表,其中包含所有城市、州、县、邮政编码(当然)以及纬度和经度。

通过使用它,我们的地址表包含与邮政编码的多对一关系。一个邮政编码可以有多个与之关联的地址。我们还使用状态表按状态对其进行分解。于是就变成了

address
 id | street | street2 | zipcode_id

zipcode
id | city | state_id | county | zip | latitude | longitude

state
 id | name | abbreviation 

然后,当用户输入邮政编码时,它会显示包含所有信息的自动完成。

然后我们做的最后一件事是标准化所有STNNW 等。我们选择将它们更改为全名,因此ST 在保存时变为STREET。我们这样做是因为您可以拥有像187 NORTH PARK 这样的街道地址,看起来像187 N PARK,这比187 PARK NE 变成187 PARK NORTH EAST 要糟糕得多。你会惊讶于地址的变化,我称之为“脏”或“脏”。

所有这些结合起来,消除了很多错误。但正如我在 cmets 中所说,我们处理的是诉讼数据,因此我们必须拥有更高的准确性和复杂性,而不仅仅是一个地址簿。

【讨论】:

  • 那么在哪里问这种问题呢?数据库设计固执己见,但我认为发现糟糕的数据库设计并不难。
  • 这取决于它有多难。数据库可能会变得相当复杂。特别是如果你有循环关系。一个好的设计不仅仅是螺母和螺栓。它必须正确地对数据建模。你不应该选择数据库模型,数据应该。
  • 例如我做了一个诉讼数据库。现在诉讼的法规就像15 USC 20 然后是15 USC 20 (a)15 USC 20 (b),然后是15 USC 20 (a)(1) 等等。本质上是一棵树,所以我使用了嵌套集合模型,这是一个真正的挑战,但它是正确的方法来做到这一点。如果没有15 USC 20 (a)15 USC 20,你就不能拥有15 USC 20 (a)(1)。这实际上是对数据库的重新做,旧的有一个单表和一个桥表,所以通过做一个树我可以只保存分支的末尾,并根据树设计找到其余部分,从而减少记录。
  • 旧的也充满了holes,其中只选择了一些法规,例如他们有15 USC 2015 USC 20 (a)(1)但没有15 USC 20 (a)所以如果你搜索@ 987654370@你不会找到诉讼。但是有了这些树,只要选择了最后一条法规,就可以正确找到路径。
  • NoINT(10) 中的 10 表示 nothing(除非您也有 ZEROFILLINT总是 32 位(4 字节)。@​​987654375@ 的范围约为 +/-20 亿;INT SIGNED 的范围为 0..4 亿。
猜你喜欢
  • 2020-01-08
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 2020-06-25
  • 2018-05-30
  • 1970-01-01
  • 2015-12-13
  • 2020-05-30
相关资源
最近更新 更多