在我的团队中进行的相当活跃的讨论中,我被认为是大多数人喜欢的主键。我们有以下几组:
- Int / BigInt哪个自动增量是足够好的主键。
- 主键应至少包含3列。
- id,GUID和人类可读的行标识符都应区别对待。
PK的最佳方法是什么?如果您能证明自己的观点,那就太棒了。上面有没有更好的方法?
编辑:任何人都有一个简单的示例/算法来生成很好地缩放行的人类可读的标识符?
在我的团队中进行的相当活跃的讨论中,我被认为是大多数人喜欢的主键。我们有以下几组:
PK的最佳方法是什么?如果您能证明自己的观点,那就太棒了。上面有没有更好的方法?
编辑:任何人都有一个简单的示例/算法来生成很好地缩放行的人类可读的标识符?
Answers:
如果要在数据库与偶尔连接的应用程序之间进行任何同步,则应使用GUID作为主键。调试有点痛苦,因此除了这种情况外,我倾向于坚持自动递增的整数。
Autoincrement int应该是默认值,不使用它们应该是合理的。
CHAR(1)
足够时,例如sex
。不用说,这是一场噩梦。
我没有看到一个答案可以指出(我认为)真正的基本要点-即,主键保证了您不会在表中获得同一真实世界实体的两个条目(例如在数据库中建模)。此观察有助于确定主键的优点和缺点。
例如,在(美国)状态名称和代码表中,名称或代码可以是主键-它们构成两个不同的候选键,并且选择其中一个(通常是较短的-代码)作为主键。首要的关键。在功能相关性(以及连接相关性-1NF到5NF)的理论中,关键的是候选键而不是主键。
举个反例,人名通常是主键的错误选择。有很多人以“约翰·史密斯”(John Smith)的名字或其他类似的名字来称呼。即使考虑到中间名(请记住:并非每个人都有一个中间名,例如,我没有),但仍有很多重复的余地。因此,人们不会将名称用作主键。他们发明了诸如社会安全号(SSN)或员工号之类的人工密钥,并使用它们来指定个人。
理想的主键应简短,独特,令人难忘且自然。在这些特征中,唯一性是强制性的;鉴于现实世界数据的限制,其他人必须灵活应对。
因此,在确定给定表的主键时,您必须查看该表代表什么。表中哪些列值集或哪些集唯一地标识表中的每一行?这些是候选键。现在,如果每个候选键由4或5列组成,那么您可能会认为这些键太笨拙而不能做成一个好的主键(主要是出于简短的考虑)。在这种情况下,您可能会引入一个替代密钥-一个人工生成的数字。通常(但不总是),一个简单的32位整数足以代替代理密钥。然后,您可以将此代理键指定为主键。
但是,您仍然必须确保将其他候选键(因为替代键也是候选键,以及所选的主键)都保持为唯一标识符-通常通过在这些列集上设置唯一约束来实现。
有时候,人们发现很难识别什么使行变得独特,但是应该做些什么,因为仅仅重复一条信息并不能使它变得更真实。而且,如果您不小心并且确实得到两(或更多)行声称要存储相同的信息,然后又需要更新该信息,则有一种危险(尤其是如果您使用游标),您只会更新一行而不是每一行,因此这些行是不同步的,没有人知道哪一行包含正确的信息。
在某些方面,这是一个很强硬的观点。
我在需要时使用GUID并没有特别的问题,但是它们往往很大(如16-64字节),并且使用频率很高。通常,一个很好的4字节值就足够了。由于每个索引页的值较少,因此使用4字节值足以满足需要的GUID浪费磁盘空间,并且甚至减慢了索引访问数据的速度,因此索引将更深,必须读取更多页才能到达索引页。信息。
这只是一个宗教问题,因为人们寻求普遍的正确答案。您的团队和该SO线程都显示出很大的分歧这一事实应该表明,有充分的理由在不同情况下使用您描述的所有解决方案。
state
(CA,TX,NY)时,您不妨使用char(2)
自然键而不是int。id
当存在完美的复合键时,不要不必要地添加“ ”替代键(在多对多表中尤其如此)。在每个表中对三列键的授权都是绝对的废话。我喜欢The Database Programmer博客作为此类信息的来源。
3个主键列?我要说的是,列应该根据业务规则的要求具有适当的唯一约束,但是我仍然需要单独的代理键。复合密钥意味着业务逻辑将输入密钥。如果逻辑发生变化,那么整个架构就被搞砸了。
有点题外话,但是我不得不强迫...
如果您的主键是GUID,请不要使其成为聚集索引。由于GUID是非顺序的,因此几乎在每次插入过程中,数据都会在磁盘上重新排列。(糟糕)如果将GUID用作主键,则它们应该是非聚集索引。
我总是使用代理键。替代密钥(通常是标识列,自动增量或GUID)是其中密钥本身不存在于数据本身中的一种。另一方面,自然键本身就是唯一标识行的键。据我所知,几乎没有真正的自然钥匙。甚至在美国,诸如SSN之类的东西都不是自然键。复合主键是一场灾难,等待发生。您无法编辑任何数据(这是使用或不使用复合键的任何自然键的主要缺点),但更糟糕的是,使用复合键后,您现在必须将该键数据保留到每个相关表中。真是浪费。
现在,为了选择代理键,我坚持使用身份列(我主要在MS SQL Server中工作)。GUID太大,Microsoft建议不要将它们用作PK。如果您有多台服务器,您要做的就是使增量为10或20,或者您认为同步/扩展到的最大服务器数量,然后为每个后续服务器上的每个表增加种子,您将永远不会发生数据冲突。
当然,由于增加的缘故,我将标识列设为BigInt(否则称为long [64位])。
进行一点数学运算,即使您将增量设为100,表中仍然可以有92,233,720,368,547,758(> 92个万亿)行。
我认为在“ Primary”键短语中使用“ Primary”一词确实是一种误导。
首先,使用“键”是表中唯一的一个属性或一组属性的定义,
然后,拥有任何键都可达到几个经常相互矛盾的目的。
为了提高需要快速在表中查找特定记录/行的查询的性能。
通过防止将代表同一逻辑实体的重复行插入表中来确保数据一致性。(这通常被称为“自然”键,并且应由相对不变的表(实体)属性组成。)
显然,任何无意义的完整非自然键(如GUID或自动生成的整数)都完全无法满足#4。
但是通常,对于许多(大多数)表,可以提供#4的完全自然的键通常会包含多个属性,并且过宽或过宽,以至于将其用于#1,#2或#3用途将导致无法接受性能后果。
答案很简单。同时使用。对其他子表中的所有联接和FK使用简单的自动生成积分键,但要确保每个需要数据一致性的表(很少有表不需要)具有备用的自然唯一键,以防止插入不一致的数据行。 ..另外,如果您始终拥有两者,那么所有反对使用自然键(如果改变的话,我都必须更改它被称为FK的每个位置)的反对意见就变得毫无意义,因为您没有为此使用它。 ..您只在一个表中使用它,因为它是PK,以避免不一致的重复数据...
对于GUID,请务必小心使用它们,因为在索引中使用guid会使索引碎片破裂。用于创建它们的最常见算法将GUID的“随机”部分放在最高有效位中。这增加了添加新行时对常规索引进行碎片整理/重新索引的要求。
您不应该做的一件事就是使用智能钥匙。这是一个密钥,其中有关记录的信息被编码在密钥本身中,并且最终会咬住您。
我在一个地方工作,那里的主键是帐户ID,它是字母和数字的组合。我不记得任何细节,但是,例如,那些类型一定的帐户将在600范围内,而另一个类型的帐户则以400开始。那太好了,直到该客户决定要求两个工作类型。或更改了他们所做的工作类型。
另一个地方,使用树中的位置作为记录的主键。因此会有类似以下的记录。
Cat1.subcatA.record1
Cat1.subcatA.record2
Cat1.subcatB.record1
Cat2.subcatA.record1
当然,客户想要的第一件事是在树上移动物品的方法。整套软件在此之前就已失效。
拜托,拜托,拜托,如果您要编写我必须维护的代码,请不要使用智能钥匙!
我喜欢自动增量作为主键。我内心深处知道这是一个解决方案,但是它确实使按数据添加时(ORDER BY ID DESC,再例如)进行排序变得如此容易。
3列听起来很难让人解析。
这就是权衡取舍-您需要多少关系功能,而不是让此处的表对于人类的询问是可以理解的(相对于存储过程或编程接口)。
自动增量对我们人类而言。:-(
应该至少有3列组成主键。
我不明白
您是否在谈论“自然钥匙”,例如“姓名和出生日期”?如果存在,那么自然键可能是理想的选择,但是大多数自然键候选者不是唯一的(几个具有相同名称的人),或者不是恒定的(有人可以更改其名称)。
Int / BigInt哪个自动增量是足够好的主键。
我更喜欢Guid。自动增量的潜在问题是该值(例如“订单ID”)是由数据库实例(例如“销售数据库”)分配的……如果以下情况不能完全起作用(相反,您开始需要复合键)您曾经需要合并由多个数据库实例创建的数据(例如,来自多个销售办事处,每个都有自己的数据库)。
RE GUID的
当心,如果这将是一个真的真的真的真的很大的数据库,大量的负载,并实现快速访问。
在我的上一份工作中,我们拥有100到5亿条记录的数据库,我们的数据库专家强烈反对GUID,并要求使用适当大小的十进制数字。他们认为(在Oracle中)字符串Guid(与十进制值)在内部存储中的大小差异将在查找中产生非常明显的差异。(更大的键=遍历更深的树)
GUID的随机性还显着降低了索引页的填充因子-这大大增加了撕裂和磁盘I / O。
自动递增列。我能够使我的代码与SQL Server或Oracle无缝协作,一个使用身份,另一个使用通过我的DAL的序列,我再也不会高兴了。我同意,如果您正在执行复制或发送数据以在以后处理时接收它,则有时需要GUID。
我一直使用代理密钥-一个称为'id'的自动递增整数。我可以看到很多理由来执行此操作,即使另一个选择很明显:
...并且没有明智的理由不这样做:
我从未想到或遇到过的明智原因总是受到欢迎...
这是经典的“取决于”。每个项目都没有一个正确的答案。我喜欢不同情况下的不同事物。这取决于我是否使用ORM及其支持的内容。它取决于整体体系结构(是否分布)。只需选择一个您认为可行的选项,然后继续讨论制表符和空格。
通过基本的定义性答案后,构成良好主键的大部分内容留给了宗教和休息室讨论。如果您具有并将始终唯一地映射到单个行的内容,则它将作为主键可以很好地工作。在那之后,还有其他注意事项:
这最后一个可能是吸引大多数人使用诸如GUID或自增整数列之类的东西的原因,因为依靠地址,电话号码,名字/姓氏等之类的东西,只是不要砍掉它。我能想到的关于人的唯一不变性就是SSN,但是我什至不能100%地确定那些永远唯一的人。
希望这有助于增加一些清晰度...
我使用主键(我认为是最好的)的方法是避免使用“默认”方法。这意味着我不只是拍打一个自动递增的整数然后称它为“一天”,我还看着问题说:“是否存在一列或一组列将始终是不固定的并且不会改变?” 如果答案是肯定的,那么我会采用这种方法。
只是有点相关,但是最近我在有小的分类表(基本上是表示代码中的ENUM的分类表)时开始做的一件事是,我将主键设为char(3)或char(4)。然后,使那些主键代表查找值。
例如,我有一个内部销售代理的报价系统。我们为“报价类别”分配了每个报价行项目之一。因此,我有一个名为“ tCostCategories”的类型查找表,其中主键为“ MTL”,“ SVC”,“ TRV”,“ TAX”, “ ODC”。查找表中的其他列存储更多详细信息,例如代码的常规英语含义,“材料”,“服务”,“旅行”,“税”,“其他直接成本”,等等。
这真的很不错,因为它不使用比int更多的空间,并且在查看源数据时,不必链接查找表即可知道该值到底是多少。例如,引用行可能看起来像:
1部件号$ 40 MTL
2其他部件号$ 29.99 SVC
3部件号2 $ 150 TRV
使用int表示类别,然后在所有行上链接1,2,3,要容易得多-您将数据摆在面前,而且性能似乎根本没有受到影响(不是因为我进行了真正的测试。)
就真正的问题而言...我喜欢RowGUID uniqueidentifiers。我不是100%对此,但不是所有行都具有内部RowGuid吗??如果是这样,那么使用RowGuid实际上会比ints(或其他任何事情)占用更少的空间。我所知道的是,如果M $在GreatPlains中使用就足够了,那么对我来说就足够了。(我应该躲起来吗?)
哦,我使用GUID的另一个原因-我使用了分层数据结构。也就是说,我有一个表“ Company”和一个表“ Vendor”,主键与其匹配。但是我还有一个“制造商”表,该表也来自“公司”。供应商和制造商通用的字段不会显示在这些表中,而是显示在公司中。在此设置中,使用int比Guids痛苦得多。至少,您不能使用身份主键。
只要我信任自然键,我就会喜欢它们。我愿意付出较小的性能价格,以便使用对主题专家有意义的键。
对于描述实体的表,应该有一个简单的自然键,用于以与主题对象相同的方式标识各个实例。如果主题没有实体之一的可信赖标识符,那么我将使用代理密钥。
对于描述关系的表,我使用了复合键,其中每个组件都引用了参与该关系的实体,因此引用了实体表中的一行。同样,使用复合键的性能影响通常很小。
正如其他人指出的那样,术语“主键”有点误导。在关系数据模型中,使用的术语是“候选键”。单个表可能有多个候选键。从逻辑上讲,每个人都和另一个人一样好。选择其中一个作为“主要”并通过该键进行所有引用只是设计者可以做出的选择。
指导期。
如果您需要扩展或需要通过其他方式分配主键,则它们将成为您的朋友。您可以为其他所有内容添加索引。
更新以澄清我的陈述。
我曾在许多不同类型的网站上工作。从小型单台服务器交易到具有多个数据库和Web服务器的大型交易。当然,有些应用程序可以将int作为主键自动递增。但是,这些都不适合我做事的模式。
使用GUID时,您可以在任何位置生成ID。它可以由远程服务器,您的Web应用程序,数据库本身甚至在多主机情况下的多个数据库中生成。
另一方面,只能在主数据库中安全地生成自动递增的INT。同样,如果您的应用程序与该备份数据库服务器紧密相关,而您不必担心横向扩展,那么这可能没关系。
当然,使用GUID意味着您必须每晚进行重新索引编制过程。但是,如果您使用的不是自动递增的INT,则无论如何都应该这样做。哎呀,即使以INT为主要对象,您也可能需要重新生成其他索引来处理碎片。因此,使用GUID不会完全增加另一个问题,因为无论如何都需要执行这些任务。
如果您查看其中的大型应用程序,您会发现一些重要的事情:它们都使用Base64编码的GUID作为键。原因很简单,GUID的使用使您可以扩展轻松地,而尝试横向扩展INT时可能会遇到很多麻烦。
我们最新的应用程序经历了一段时间的沉重插入,持续了大约一个月。之后,90%以上的查询全部选择用于报告。为了增加容量,我可以在这个大插入期间启动其他数据库服务器。然后将它们轻松合并到一个数据库中进行报告。尝试使用INT来做到这一点绝对是一场噩梦。
坦率地说,每当您对数据库进行集群或设置复制时,DB服务器将始终要求您在表上具有GUID。因此,如果您认为您的系统可能需要扩展,请选择一个好的系统。
无论您是否意识到,这都是一个复杂的主题。可能属于此StackOverflow常见问题解答的部分。
我在这里不应该问什么问题?
避免提出主观,争论或需要扩展讨论的问题。这是一个可以回答问题的地方!
这已经辩论了多年,并将继续辩论多年。我所看到的唯一共识是,答案是可以预测的,具体取决于您是否问OO对象(GUID是唯一的方法!),数据建模者(自然键是唯一的方法!),或面向性能的DBA(INT是唯一的方法!)。