数据库设计和非数字主键的使用

问题描述 投票:14回答:11

我目前正在为客户和网站管理应用程序设计数据库表。我的问题是关于使用主键作为表的功能部分(并没有为每个表分配“ID”号)。

例如,以下是目前为止数据库中的四个相关表,其中一个表使用传统的主键号,其他表使用唯一的名称作为主键:

--
-- website
--
CREATE TABLE IF NOT EXISTS `website` (
  `name` varchar(126) NOT NULL,
  `client_id` int(11) NOT NULL,
  `date_created` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `notes` text NOT NULL,
  `website_status` varchar(26) NOT NULL,
  PRIMARY KEY  (`name`),
  KEY `client_id` (`client_id`),
  KEY `website_status` (`website_status`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- website_status
--
CREATE TABLE IF NOT EXISTS `website_status` (
  `name` varchar(26) NOT NULL,
  PRIMARY KEY  (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `website_status` (`name`) VALUES
('demo'),
('disabled'),
('live'),
('purchased'),
('transfered');

--
-- client
--
CREATE TABLE IF NOT EXISTS `client` (
  `id` int(11) NOT NULL auto_increment,
  `date_created` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `client_status` varchar(26) NOT NULL,
  `firstname` varchar(26) NOT NULL,
  `lastname` varchar(46) NOT NULL,
  `address` varchar(78) NOT NULL,
  `city` varchar(56) NOT NULL,
  `state` varchar(2) NOT NULL,
  `zip` int(11) NOT NULL,
  `country` varchar(3) NOT NULL,
  `phone` text NOT NULL,
  `email` varchar(78) NOT NULL,
  `notes` text NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `client_status` (`client_status`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

--
-- client_status
---
CREATE TABLE IF NOT EXISTS `client_status` (
  `name` varchar(26) NOT NULL,
  PRIMARY KEY  (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `client_status` (`name`) VALUES
('affiliate'),
('customer'),
('demo'),
('disabled'),
('reseller');

如您所见,4个表中的3个使用其“名称”作为主键。我知道这些将永远是独一无二的。在其中两个案例中(* _status表)我基本上使用ENUM的动态替换,因为状态选项将来可能会改变,而对于'网站'表,我知道网站的'名称'将始终独一无二。

我想知道这是否是合理的逻辑,当我知道名称总是一个唯一的标识符或灾难的食谱时,摆脱表ID?我不是一个经验丰富的DBA所以任何反馈,批评等都会非常有帮助。

感谢您抽时间阅读!

sql database database-design primary-key primary-key-design
11个回答
16
投票

有两个原因我总是在查找/ ENUM表中添加ID号:

  1. 如果您使用名称引用单个列表,则可以通过使用约束来更好地服务
  2. 如果要重命名其中一个client_status条目会发生什么?例如如果您想将名称从“affiliate”更改为“affiliate user”,则需要更新不必要的客户端表。 ID号用作参考,名称是描述。

在网站表中,如果您确信该名称将是唯一的,则可以将其用作主键。我个人仍然会分配一个数字ID,因为它减少了外键表中使用的空间,我发现它更容易管理。

编辑:如上所述,如果重命名网站名称,您将遇到问题。通过将其作为主键,即使不是不可能在以后更改它,也将非常困难。


0
投票

在决定表格中的键之前,应该考虑几点

  • 当您使用引用(外键)时,数字键更合适,因为您不使用外键,在您的情况下可以使用非数字键。
  • 非数字键比数字键使用更多空间,可能会降低性能。
  • 数字键使db看起来更容易理解(仅通过查看最后一行就可以轻松地知道行数)

-1
投票

你永远不知道你工作的公司什么时候突然爆发增长,你必须在一夜之间聘请5名开发人员。您最好的选择是使用数字(整数)主键,因为它们对于整个团队来说将更容易使用。如果数据库增长,将有助于您的性能。如果必须中断记录并对其进行分区,则可能需要使用主键。如果要添加带有日期时间戳记的记录(如每个表所示),并且代码中某处错误地更新该字段,则唯一的方法是确认记录是否以正确的顺序输入以检查主要键。使用INT主键可能还有10多个TSQL或调试原因,其中最重要的是编写一个简单的查询来选择输入表中的最后5条记录。


13
投票

制作天然的PRIMARY KEY时,请确保它们的独特性在您的控制之下。

如果你绝对肯定你永远不会有唯一性违规,那么将这些值用作PRIMARY KEY是可以的。

由于website_statusclient_status似乎是由您生成并且仅由您使用,因此将它们用作PRIMARY KEY是可以接受的,尽管使用长按键可能会影响性能。

website的名字似乎在外部世界的控制之下,这就是为什么我要把它变成一个普通的领域。如果他们想重命名他们的website怎么办?

反例将是SSNZIP代码:产生它们的不是你,并且不能保证它们不会被复制。


9
投票

Kimberly Tripp在创建聚簇索引和选择主键(相关问题,但并非总是完全相同)的问题上有一系列优秀的博客文章(GUIDs as PRIMARY KEYs and/or the clustering keyThe Clustered Index Debate Continues)。她的建议是聚集索引/主键应该是:

  1. 独特(否则无用)
  2. 窄(密钥用于所有非聚集索引和外键关系)
  3. 静态(您不想更改所有相关记录)
  4. 始终增加(因此新记录总是添加到表的末尾,而不必插入中间)

使用“名称”作为键,虽然它似乎满足#1,但不满足其他三个中的任何一个。

即使对于您的“查询”表,如果您的老板决定将所有关联公司更改为合作伙伴,该怎么办?您必须修改数据库中使用此值的所有行。

从性能的角度来看,我可能最关心的是一个关键是狭窄的。如果您的网站名称实际上是一个很长的URL,那么这可能会使任何非聚集索引的大小以及将其用作外键的所有表格都很大。


3
投票

除了已经提出的所有其他优点之外,我还要再添加一个警告,不要在SQL Server中使用大字段作为集群键(如果你没有使用SQL Server,那么这可能不适用于你) 。

我添加这个是因为在SQL Server中,默认情况下表上的主键也是聚类键(如果你想知道它,你可以改变它,但大多数情况下,它没有完成)。

确定SQL Server表的物理排序的集群键也将添加到该表上的每个非聚集索引。如果你只有几百到几千行和一个或两个索引,这不是什么大问题。但是如果你真的有大量数据库的表,并且可能有很多索引来加速查询,这确实会导致大量磁盘空间和服务器内存不必要地浪费。

例如。如果你的表有1000万行,10个非聚集索引,你的聚类键是26个字节而不是4个(对于INT),那么你就浪费了10个mio。 10 x 22字节,总共22亿字节(或约2.2 GB) - 这不再是花生!

同样 - 这仅适用于SQL Server,并且只有当您拥有非常大的表时才会有大量非聚集索引。


2
投票

“如果你绝对肯定你永远不会有唯一性违规,那么可以将这些值用作PRIMARY KEY。”

如果您完全确定永远不会遇到唯一性违规,那么请不要费心去定义密钥。


1
投票

就个人而言,我认为你会遇到这个想法遇到麻烦。当你最终得到更多的父子关系时,你会在名字改变时最终得到大量的工作(因为他们总是会迟早)。当网站名称发生变化时,必须更新包含数千行的子表时,可能会有很大的性能损失。你必须计划如何确保这些变化发生。否则,网站名称会更改(我们让名称过期而其他人购买它。)要么因为外键约束而中断,要么需要以自动方式(级联更新)通过系统传播更改。如果您使用级联更新,那么在处理大型chage时,您可能会突然停止系统。这不被认为是一件好事。使用id作为关系然后在名称字段上放置唯一索引以确保它们保持唯一性确实更有效和高效。数据库设计需要考虑维护数据完整性以及这将如何影响性能。

另一件需要考虑的事情是网站名称往往比一些字符长。这意味着使用连接的id字段和连接的名称之间的性能差异可能非常大。您必须在设计阶段考虑这些事情,因为当您拥有一个包含数百万条记录的生产系统超时并且修复完全重构数据库并重写所有SQL时,更改为ID为时已晚码。不是十五分钟就能解决的问题,让网站再次运行。


1
投票

这似乎是一个非常糟糕的主意。如果您需要更改枚举值,该怎么办?这个想法是使它成为一个关系数据库,而不是一组平面文件。此时,为什么有client_status表?此外,如果您正在使用应用程序中的数据,通过使用类似GUID或INT的类型,您可以验证类型并避免错误数据(在验证类型方面)。因此,它是阻止黑客攻击的众多方法中的另一个。


1
投票

我认为,一个能够抵抗腐败的数据库,即使它运行得慢一点,也比没有腐败的数据库好。

通常,代理键(例如任意数字标识符)会破坏数据库的完整性。主键是识别数据库中行的主要方式;如果主键值没有意义,则约束无意义。因此,任何引用代理主键的外键也是可疑的。每当您必须检索,更新或删除单个行(并保证只影响一个行)时,您必须使用主键(或另一个候选键);当存在有意义的替代密钥时,必须弄清楚代理键值是什么,这对于用户和应用程序来说是一个冗余且具有潜在危险的步骤。

即使这意味着使用复合键来确保唯一性,我也会主张尽可能使用有意义的,自然的属性集作为主键。如果您还需要记录属性,为什么要添加另一个?也就是说,当没有自然,稳定,简洁,保证是唯一的密钥(例如,对于人)时,代理密钥就可以了。

如果您的DBMS支持,您还可以考虑使用索引键压缩。这可能非常有效,特别是对于复合键上的索引(想想trie数据结构),特别是如果最不具选择性的属性可以首先出现在索引中。


1
投票

我想我同意cheduardo的意见。我参加数据库设计课程已有25年,但我记得被告知数据库引擎可以更有效地管理和加载使用字符键的索引。关于数据库必须在更改密钥时更新数千条记录以及由更长密钥占用的所有添加空间然后必须跨系统传输的注释,假定密钥实际存储在记录中并且无论如何它不必跨系统传输。如果在表的列上创建索引,我不认为该值存储在表的记录中(除非您设置了一些选项)。

如果您有一个表的自然键,即使它偶尔更改,创建另一个键会产生冗余,可能导致数据完整性问题,并实际创建需要跨系统存储和传输的更多信息。我为一个决定将本地应用程序设置存储在数据库中的团队工作。它们具有每个设置的标识列,节名称,键名和键值。他们有一个存储过程(另一个圣战)来保存设置,以确保它不会出现两次。我还没有找到一个我会使用设置ID的情况。但是,我最终得到了多个记录,这些记录具有导致我的应用程序失败的相同部分和密钥名称。是的,我知道可以通过在列上定义约束来避免这种情况。

© www.soinside.com 2019 - 2024. All rights reserved.