同性婚姻:数据库工程的角度
今天看到一篇妙文:
Gay marriage: the database engineering perspective
这文章说明了良好的设计来自于对问题的深入理解,对问题的深入探讨不是无用古板的学究行为,反而会使得程序灵活而强大。
试着将此文的大概内容翻译如下:
问题:设计一个反映夫妻关系的数据库。
模式1:
`males` - `id` - `forename` - `surname` - `birthdate` - `wife_id` (foreign key references column `females`.`id`, may be NULL if male is unmarried)
`females` - `id` - `forename` - `surname` - `birthdate` - `husband_id` (foreign key references column `males`.`id`, may be NULL if female is unmarried)
简洁的两个表,一个男人,一个女人,各有一个id作为主键,并有一个外键表示婚姻关系。对于某人的夫/妻是谁可以直接得到,看起来完美无缺,但是且不说功能,这种方案最严重的问题是二义性!在数据库中如果某人的wife_id非空,但其wife对于的husband_id为空,或者和其husband的数据不符合,信息冗余造成了二义性!实际数据库应用中,实在是太可能发生了。
方案2
`males` - `id` - `forename` - `surname` - `birthdate` - `wife_id` (unique foreign key references column `females`.`id`, may be NULL if male is unmarried)
`females` - `id` - `forename` - `surname` - `birthdate` 看起来完美的解决了二义性问题,很简单,只要夫妻一方存有婚姻关系的信息就好了,两方都保存的话,反而有可能造成矛盾。现在多一点点功能需求,比如,希望增加一个结婚日期信息,应该怎么办呢?
方案3
`males` - `id` - `forename` - `surname` - `birthdate` - `wife_id` (foreign key references column `females`.`id`, may be NULL if male is unmarried)
- `marriage_date` (may be NULL if male is unmarried) `females` - `id` - `forename` - `surname` - `birthdate` 很简单,在存储了婚姻信息的表上加一行即可,考虑到现在的离婚率,再加一行离婚日期也是有必要的。
方案4
`males` - `id` - `forename` - `surname` - `birthdate` - `wife_id` (foreign key references column `females`.`id`, may be NULL if male is unmarried)
- `marriage_date` (may be NULL if male is unmarried) - `divorce_date` (may be NULL if male is unmarried or married but not yet divorced) `females` - `id` - `forename` - `surname` - `birthdate` 好了,但是显然,需求变更是无止境的,很快,有的用户希望存储结婚地点,证婚人等等信息。而这些信息其实是属于婚姻关系这个实体本身的,而不是属于某个males实体。这提示我们需要多增加一张表。更不用说在婚姻信息存储在males表字段的情况下,如果某个男人结婚又离婚,然后再结婚,上一次婚姻的信息就会丢掉,这是难以接受的!
方案5
`males` - `id` - `forename` - `surname` - `birthdate` - `marriage_id` (foreign key references column `marriages`.`id`, may be NULL if male is unmarried)
`females` - `id` - `forename` - `surname` - `birthdate` - `marriage_id` (foreign key references column `marriages`.`id`, may be NULL if female is unmarried)
`marriages` - `id` - `marriage_date` - `divorce_date` (NULL if marriage not ended) 这个方案把婚姻关系实体新建了一张表,并在男女双方的表中增加了一个marriage_id,分表无疑是明智的,但是显然又带来了二义性问题,试想一下夫妻双方的marriage_id不一致!此外这种方式并没解决再婚带来的信息丢失问题。
方案6
`males` - `id` - `forename` - `surname` - `birthdate` `females` - `id` - `forename` - `surname` - `birthdate` `marriages` - `id` - `husband_id` (foreign key references column `males`.`id`)
- `wife_id` (foreign key references column `females`.`id`)
- `marriage_date` - `divorce_date` (NULL if marriage not ended) 将双方的id作为婚姻关系的外键显然是明智的,marriage实体包含了足够的信息,这种方案完美的解决了男女之间进行一夫一妻且可离婚再婚的婚姻关系的信息存储问题,伟大的进步!但是这个有没有改进的余地呢?males和females的表似乎一模一样了啊。
方案7
`humans` - `id` - `forename` - `surname` - `birthdate` - `sex` ("male" or "female") `marriages` - `id` - `husband_id` (foreign key references a male in column `humans`.`id`)
- `wife_id` (foreign key references a female in column `humans`.`id`)
- `marriage_date` - `divorce_date` (NULL if marriage not ended) 既然一样,当然可以合并,将男女表一并合并为humans,这种男女一视同仁的信息存储方式,为走向同性婚姻之路解决重大问题!
方案8
`humans` - `id` - `forename` - `surname` - `birthdate` - `sex` ("male" or "female") `marriages` - `id` - `partner_1_id` (foreign key references column `humans`.`id`)
- `partner_2_id` (foreign key references column `humans`.`id`)
- `marriage_date` - `divorce_date` (NULL if marriage not ended) 只需要把wife_id和husband_id换成partner_1_id和partner_2_id就好了!完美的主意,这似乎已经可以拿去给政府使用了,但是且慢!sex只有males和females,这真的符合实际情况么?如果学过生物学,自然就知道除了男女,还有无性人和镶嵌体,更不要说变性手术了!
方案9
`humans` - `id` - `forename` - `surname` - `birthdate` - `sex_id` (foreign key references column `sexes`)
`marriages` - `id` - `partner_1_id` (foreign key references column `humans`.`id`)
- `partner_2_id` (foreign key references column `humans`.`id`)
- `marriage_date` - `divorce_date` (NULL if marriage not ended) `sexes` - `id` - `string` 把sex单独拉出一个表来,好多了,但是这合适吗?sex是一个生物色彩浓厚的词,和关系中的gender在很多时候未必相同。忽视性倒错者的解决方案不是好的解决方案。再来!
方案10
`humans` - `id` - `forename` - `surname` - `birthdate` - `sex_id` (foreign key references column `sexes`)
- `gender_id` (foreign key references column `genders`)
`marriages` - `id` - `partner_1_id` (foreign key references column `humans`.`id`)
- `partner_2_id` (foreign key references column `humans`.`id`)
- `marriage_date` - `divorce_date` (NULL if marriage not ended) `sexes` - `id` - `string` `genders` - `id` - `string` 嗯 多了一个genders表,很好,但是如果我们还期望其他信息该怎么办,自然可以继续加表!但是,我们已经离题了,性倒错者,同性恋者的存在,并不是表明我们要在表示婚姻关系的数据库中存储性别,性取向等信息,恰恰相反,这些情况的存在正好证明了婚姻关系和性别,性取向的无关。这些信息并不是表明婚姻关系所必须的,根据奥卡姆剃刀原则,可以直接删去。
方案11
`humans` - `id` - `forename` - `surname` - `birthdate` `marriages` - `id` - `partner_1_id` (foreign key references column `humans`.`id`)
- `partner_2_id` (foreign key references column `humans`.`id`)
- `marriage_date` - `divorce_date` (NULL if marriage not ended) 删去了各种信息,性别啊,取向啊,在爱情前面已经不重要了,婚姻……终于被还原成了简单的二人关系。但是...婚姻真的只是二人关系吗?古人的三妻四妾难道不应该被考虑吗?这种设计显然无法满足古代家谱研究者的需求啊!阿拉伯国家的民政局拒收此份软件!
方案12
`humans` - `id` - `forename` - `surname` - `birthdate` `marriages` - `id` - `marriage_date` - `divorce_date` (NULL if marriage not ended) `marriage_partners` - `id` - `human_id` (foreign key references column `humans`.`id`)
- `marriage_id` (foreign key references column `marriages`.`id`)
好了,一个人,既是一个人,又是一个丈夫(妻子)……准确的说,是一个婚姻关系的参与者。于是把婚姻参与者这个实体建立成表,好像可以解决问题了。且慢!这种情况下,结婚日期还是应该属于婚姻关系这个实体吗?周扒皮三月份娶了正房,九月份又娶了小妾,这显然需要修改表结构。
方案13
`humans` - `id` - `forename` - `surname` - `birthdate` `marriages` - `id` `marriage_partners` - `id` - `human_id` (foreign key references column `humans`.`id`)
- `marriage_id` (foreign key references column `marriages`.`id`)
- `marriage_date` - `divorce_date` (NULL if still in marriage) 稍作调整,好了!问题到这个地方可以得到解决了,不管几个人怎么婚都不会有问题了。某个人,在加入了一个婚姻关系之后,就成为了一个婚姻关系的参与者,他/她/它拥有着自己在这个婚姻关系中作为参与者的相关信息,那怕是反复的加入一段婚姻关系,或者加入多个婚姻关系,所有信息都会得到完整的记录。形式和内容在这个方案中得到了统一。方案13看来已经解决了人类社会大部分的婚姻关系了!(目前的限制是参与者必须是人,但是人类道德水平无下限,你懂的!)
方案14
`humans` - `id` - `forename` - `surname` - `birthdate` `relations` - `id` - `type_id`(foreign key references column `types`) `relation_partners` - `id` - `human_id` (foreign key references column `humans`.`id`)
- `relations_id` (foreign key references column `relations`.`id`)
- `join_date` - `leave_date` (NULL if still in relation) `types` - `id` - `string` 方案13实际上已经描述了一个相当常见的场景,召集一场团购,约一次集体踏青,开桌游大会,在日常生活里都比三妻四妾常见许多,因此,将方案13略加扩充,就变成一个可以存储多种多人活动信息的模式。如何,是不是有意义了很多呢?
转载于:https://www.cnblogs.com/archen1983/archive/2011/06/14/2080197.html
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
