我应使用代理键(id= 1)还是自然主键(tag='sqlalchemy') 作为我的sqlalchemy模型?
在数据库方面,我了解到,只要主键不是特别长,自然主键是更好的选择,因为太长会导致索引性能问题。不过,当我通过谷歌代码搜索查看使用sqlalchemy的项目时,我几乎总是能看到类似这样的代码:
class MyClass(Base):
__tablename__ = 'myclass'
id = Column(Integer, primary_key=True)
如果我有一个简单的类,比如标签(tag),我只打算存储一个值,而且这个值需要唯一,那么在使用sqlalchemy时,使用一个替代主键有什么好处呢?我正在阅读的一本SQL书提到,ORM(对象关系映射)是“反模式”的一种合理用法,但他想象中的ORM更像是ActiveRecord或Django。在我的模型中,这个问题出现了几次,这里有一个例子:
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True) #should I drop this and add primary_key to Tag.tag?
tag = Column(Unicode(25), unique=True)
....
在我更大的关系模型中,标签(Tag)与其他对象有多个多对多的关系。因此,会有一些中间表需要存储更长的主键。我应该选择标签(tag)还是ID作为我的主键呢?
3 个回答
每当我看到有人过度使用替代键时,我就会想起Roy Hann在这个话题上写的博客文章,特别是第二篇和第三篇:
http://community.actian.com/forum/blogs/rhann/127-surrogate-keys-part-2-boring-bit.html
http://community.actian.com/forum/blogs/rhann/128-surrogate-keys-part-3-surrogates-composites.html
我强烈建议大家去看看这些文章,因为它们来自一位在数据库领域工作了几十年的专家。
现在替代键的使用让我想起21世纪初的那些年,那时候人们几乎把XML用在了所有地方,有些地方用得着,有些地方根本不需要。
我个人在很多地方更喜欢用替代键,主要有两个原因:第一,整数键通常更小、更快;第二,更新数据时不需要级联更新。第二点对于你正在做的事情来说非常重要。如果有很多多对多的表引用了标签表,那么要记住,如果有人想要更新一个标签(比如修正拼写错误、大小写问题,或者使用更具体或更模糊的词等),那么这个更新就需要在所有相关的表中同时进行。
我并不是说你绝对不能使用自然键——如果我确定自然键永远不会被更改,我会考虑使用自然键。只要确保这一点,否则维护起来会很麻烦。
虽然ORM(对象关系映射)或者编程语言让某些操作变得简单,但我认为选择主键是一个数据库设计的问题,这和ORM没有直接关系。更重要的是要确保数据库的结构设计合理。毕竟,数据库的使用寿命通常比访问它的代码要长。
如果想了解如何选择主键,可以在Stack Overflow(和谷歌)上搜索一些更一般的问题,比如:https://stackoverflow.com/search?q=primary+key+natural+surrogate+database-design (替代键与自然键/业务键, 关系数据库设计问题 - 替代键还是自然键?, 什么时候不使用替代主键?,等等)
我假设Tag
表不会很大或者变化很频繁。在这种情况下,我会尝试使用tag
作为主键,除非有一些重要原因需要添加一个对最终用户“不可见”的主键,例如:
在真实数据下性能差(这是经过测量的,而不是想象的),
标签名称经常变动(但即使如此,我还是会用第一个使用的标签名称生成一个唯一的字符串作为主键),
在后台合并标签时不可见(但,见上一个点),
在你的关系数据库管理系统中处理不同的排序规则时遇到问题(但,...)
...
总的来说,我观察到人们在这方面往往会犯两个方向的错误:
使用复杂的多字段“自然”键(其中某些字段本身是模糊的数字),而表中的每一行都有自己的身份,应该使用自己的替代ID,
为所有东西引入随机的数字代码,而不是使用简短且有意义的字符串。
有意义的主键值——如果可能的话——在手动浏览数据库时会非常有用。你不需要通过多个连接来搞清楚你的数据。