数据库表可以没有主键吗?
Posted
技术标签:
【中文标题】数据库表可以没有主键吗?【英文标题】:Can a database table be without a primary key? 【发布时间】:2011-01-31 17:12:27 【问题描述】:谁能告诉我关系数据库(如 mysql / SQL SERVER)中的表是否可以没有主键?
例如,我可以有表day_temperature
,我在其中注册temperature
和time
。我看不出有这样一个表的主键的原因。
【问题讨论】:
也没有真正的理由不拥有一个。 日期和时间似乎是主键。 ***.com/questions/840162/… @bodacydo - 你刚才描述的是一个主键。如果您正在记录温度(每天一个),则 PK 将成为日期时间字段。如果没有它,当您尝试查询数据表时会遇到非常糟糕的性能。 【参考方案1】:即使您不向 MySQL 中的 InnoDB 表添加主键,MySQL 也会向该表添加隐藏的聚集索引。如果没有定义主键,MySQL 会定位第一个唯一索引,其中所有键列都不是 NULL,InnoDB 将其用作聚集索引。
如果表没有主键或合适的 UNIQUE 索引,InnoDB 会在内部在包含行 ID 值的合成列上生成聚集索引 GEN_CLUST_INDEX。
https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html
【讨论】:
【参考方案2】:根据您的回答,我会考虑三个选项:
在两个列上放置一个 PK,这样每次可能只有一个温度,反之亦然。此解决方案允许多行具有相同的温度或相同的时间,只是不会有任何两行具有相同的温度和时间。 根本不要放置PK,但要在两个列上放置唯一索引。一个包含两个列的唯一索引。这将允许 temp 和 time 为空,但会产生更多空间来维护索引。如果您有大量读取,这两个选项最适合检索速度,但会导致较低的插入率,因为索引也必须更新。
根本不要放任何索引,也不要PK。这对于插入是最好的,但对搜索非常不利。用于记录由另一个人完成检索的日志 机制或当插入设备不需要检查重复时。此外,在这里考虑基数并考虑使用自动递增数字的未来后果非常重要。如果您打算进行很多插入,那么即使是自动递增的 unsigned bigint 也会有风险,因为它最终会用完。在您的示例中,我猜您将每天保存数据 - 保存多长时间?如果你每分钟保存一次温度,这将是有问题的......所以我将把它作为一个极端的例子。
我想最好从表中考虑您需要什么。您是否正在为每一分钟的温度进行全年的“保存并忘记”?您是否会在业务逻辑中的实时决策中经常使用此表?我认为最好将实时所需的数据(oltp)与很少需要的长期保存数据分开,并且允许其检索延迟很高(olap)。甚至值得将数据复制到两个不同的表中,一个索引很重,有时会被擦除以控制基数,第二个实际上保存在几乎没有索引的磁磁盘上(可以从您的主 fs 到另一个 fs)。
【讨论】:
【参考方案3】:像往常一样视情况而定。
表没有有主键。 更重要的是拥有正确的索引。在数据库引擎上取决于主键如何影响索引(即为主键列/列创建唯一索引)。
但是,在您的情况下(以及 99% 的其他情况),我会添加一个新的自动增量唯一列,例如 temp_id
,并使其成为代理主键。
它让维护这个表变得更容易——例如查找和删除记录(即重复的记录)——相信我——因为每个表都需要修复问题:(。
【讨论】:
【参考方案4】:在mysql上复制数据库时,没有主键的表可能会导致复制延迟。
http://lists.mysql.com/mysql/227217
使用 ROW 或 MIXED 时最常见的错误是未能 验证您要复制的每个表都有一个 PRIMARY KEY 它。这是一个错误,因为当 ROW 事件(例如 上面记录的)被发送到从站,而不是主站的副本 表的从属副本也没有表上的 PRIMARY KEY, 无法轻松识别您想要的唯一行 复制以改变。
【讨论】:
【参考方案5】:我在一张桌子上遇到了同样的问题。
问题是 PK 应该由表的所有行组成,这一切都很好,但这意味着表的大小会随着每一行的插入而增长得非常快。
我选择没有 PK,但只有在我进行查找的行上有一个索引。
【讨论】:
【参考方案6】:我会包括一个代理/自动增量键,特别是如果有任何重复时间/温度读数的可能性。您将没有其他方法来唯一标识重复的行。
【讨论】:
【参考方案7】:从技术上讲,您可以声明这样的表。
但在你的情况下,time
应该变成PRIMARY KEY
,因为在同一时间有不同的温度可能是错误的,而且多次使用相同的温度可能没有用。
从逻辑上讲,每个表都应该有一个PRIMARY KEY
,以便您可以区分两条记录。
如果您的数据中没有候选键,只需创建一个替代键(AUTO_INCREMENT
、SERIAL
或您的数据库提供的任何内容)。
没有PRIMARY KEY
的唯一借口是日志或类似的表,它是一个很重的DML
的对象,并且在其上建立索引会影响性能超出容忍水平。
【讨论】:
如果没有 PK,Celko 会说它不是一张桌子! @Martin:关系数据库和SQL
使用多重集而不是集,所以它仍然是一个表。但是,在内部,这仍然是一个集合(应该区分记录),代理 PK
只是有助于使记录与外部区分开来。
+1 有趣的是我以前从未听说过。应该有人告诉乔! eggheadcafe.com/software/aspnet/31906606/…
@Martin:也在我的博客中:explainextended.com/2009/03/14/deleting-duplicates 但是祝你好运在MySQL
中做同样的事情:)【参考方案8】:
我有一个不需要主键的表的更好示例 - 连接表。假设我有一个名为“capabilities”的表,还有一个名为“groups”的表,我想要一个 joiner 表来告诉我所有组可能具有的所有功能,所以它基本上是
create table capability_group
( capability_id varchar(32),
group_id varchar(32));
没有理由为此设置主键,因为您从不处理单行 - 您要么想要给定组的所有功能,要么想要给定功能的所有组。最好在 (capabilty_id,group_id) 上有一个唯一的约束,并在两个字段上分别索引。
【讨论】:
但是通过给它一个唯一的约束,你承认它是一个候选键,那么为什么不把它变成 PK 呢?在实现方面,无论如何它都会使用索引来强制执行约束。 在这种情况下,(capability_id, group_id)
应该合成一个PRIMARY KEY
。这也将使该表对于具有集群存储的引擎更有效(InnoDB
、SQL Server
(集群 PK)、Oracle
(ORGANIZATION INDEX
))【参考方案9】:
您不需要 PK,但建议您拥有一个。这是识别唯一行的最佳方式。有时您不想要自动增量 int PK,而是在其他东西上创建 PK。例如,在您的情况下,如果每次只有一个唯一行,则应按时创建 PK。它使基于时间的查找速度更快,并且确保它们是唯一的(您可以确保不违反数据完整性):
【讨论】:
【参考方案10】:时间将成为您的主键。它将帮助索引该列,以便您可以根据日期范围查询数据。 PK 是最终使您的行独一无二的原因,因此在您的示例中,日期时间就是 PK。
【讨论】:
【参考方案11】:如果存在重复条目(例如同一时间)的可能性不成问题,并且您不希望查询特定记录或记录范围,则可以不使用任何类型的键。
【讨论】:
以上是关于数据库表可以没有主键吗?的主要内容,如果未能解决你的问题,请参考以下文章