Van*_*nel 16 sql-server primary-key unique-constraint
我正在开发一个 SQL Server 2012 数据库,我对 nvarchar 列作为主键有疑问。
我有这张桌子:
CREATE TABLE [dbo].[CODES]
(
[ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
[CODE_LEVEL] [tinyint] NOT NULL,
[CODE] [nvarchar](20) NOT NULL,
[FLAG] [tinyint] NOT NULL,
[IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED
(
[CODE_LEVEL] ASC,
[CODE] ASC
)
)
Run Code Online (Sandbox Code Playgroud)
但现在我想使用[CODE]
列作为主键并删除[ID_CODE]
列。
如果我有一个NVARCHAR
专栏,有什么问题或惩罚PRIMARY KEY
吗?
[CODE]
列值必须是唯一的,所以我认为我可以UNIQUE
为该列设置约束。
我是否必须用[CODE]
作主键,还是UNIQUE
在[CODE]
列上设置约束更好?
Sol*_*zky 16
是的,对于主键使用字符串而不是数字类型绝对会产生负面影响,如果该 PK 是集群的(在您的情况下确实如此),则更是如此。但是,您看到使用字符串字段的效果的程度取决于 a) 该表中有多少行,以及 b) 其他表中有多少行是外键到这个 PK 的。如果您在该表中只有 10k 行,而在通过该字段 FK 到该表的其他几个表中只有 100k 行,那么它可能不会那么明显。但随着行数的增加,这些影响肯定会变得更加明显。
您需要考虑将聚集索引中的字段转移到非聚集索引。因此,您不只是查看每行最多 40 个字节,而是查看 (40 * some_number) 个字节。并且在任何 FK 表中,您在行中都有相同的 40 个字节,而且该字段上通常会有一个非聚集索引,因为它在 JOIN 中使用,所以现在它在 FK 到的任何表中实际上翻了一番这个。如果有人倾向于认为 40 字节 * 100 万行 * 10 个副本没什么可担心的,请参阅我的文章磁盘便宜!奥利?其中详细说明了受此决定影响的所有(或至少大部分)领域。
另一件要考虑的事情是对字符串进行过滤和排序,特别是在不使用二进制排序规则时(我假设您使用的是数据库默认值,通常不区分大小写)比使用INT
/时效率低得多(即需要更长的时间)BIGINT
。这会影响在此字段上过滤/加入/排序的所有查询。
因此,CHAR(5)
对于集群 PK使用类似的东西可能没问题,但主要是如果它也定义为COLLATE Latin1_General_100_BIN2
(或类似的东西)。
价值会[CODE]
永远改变吗?如果是,则更有理由不将其用作 PK(即使您确实将 FK 设置为ON UPDATE CASCADE
)。如果它不能或永远不会改变,那很好,但仍然有足够的理由不将其用作集群 PK。
当然,该问题的措辞可能不正确,因为您目前的 PK 中似乎已经有此字段。
无论如何,到目前为止,您最好的选择是[ID_CODE]
用作集群 PK,在相关表中使用该字段作为 FK,并保留[CODE]
为 a UNIQUE INDEX
(这意味着它是一个“备用键”)。
在对此答案的评论中更新基于此问题的更多信息:
如果我使用 [CODE] 列查找表,[ID_CODE] 作为 PRIMARY KEY 是最好的选择吗?
这一切都取决于很多因素,其中一些我已经提到过,但将重申:
主键是识别单个行的方式,无论它是否被任何外键引用。您的系统在内部识别行的方式与您的用户识别自己/该行的方式有关,但不一定相同。任何具有唯一数据的 NOT NULL 列都可以工作,但需要考虑实用性问题,尤其是当 PK 实际上被任何 FK 引用时。例如,GUID 是独一无二的,有些人出于各种原因真的很喜欢使用它们,但它们对于聚集索引来说非常糟糕(NEWSEQUENTIALID
更好,但并不完美)。另一方面,GUID 作为备用键很好,应用程序使用它来查找行,但 JOIN 仍然使用 INT(或类似)PK 完成。
到目前为止,您还没有告诉我们该[CODE]
字段如何从各个角度融入系统,除了现在提到这是您查找行的方式之外,但这是针对所有查询还是仅针对某些查询?因此:
关于[CODE]
价值:
关于这张表:
[CODE]
或[ID_CODE]
)是否在其他表中使用,即使没有明确外键?[CODE]
是用于获取单个行的唯一字段,那么该[ID_CODE]
字段的用途是什么?如果不使用它,为什么首先要使用它(这可能取决于“该[CODE]
领域是否会改变?”的答案)?这个决定不能纯粹根据“NVARCHAR 是还是否?”的问题做出。我再说一遍,总的来说,我认为这不是一个好主意,但肯定有一些时候它是好的。鉴于此表中的字段很少,因此不太可能有更多索引,或者至少没有很多索引。因此,您可以使用任何一种方式[CODE]
作为聚集索引。如果没有其他表引用此表,那么您也可以将其设为 PK。但是,如果其他表确实引用了这个表,那么我会选择该[ID_CODE]
字段作为 PK,即使是非聚集的。
您必须将概念分开:
主键是一种设计理念,是表中条目的逻辑属性。它在表条目的生命周期内应该是不可变的,并且应该是应用程序中用来引用条目的键。
聚集索引是一种存储概念,一种物理属性。它应该是查询最常见的访问路径,它应该满足大多数情况下的覆盖索引,并尽可能多地满足范围查询。
主键不需要是聚集索引。您可以拥有ID_CODE
PK 和(CODE_LEVEL, CODE)
集群密钥。或者反过来。
较大的聚集键会产生一些负面影响,因为较宽的键意味着索引页上的密度较低,并且所有非聚集索引消耗的大小较大。在这个话题上已经有大量的墨水,例如。从更多关于聚簇键的考虑开始——聚簇索引的争论还在继续!.
但问题的要点是,聚集索引键的选择主要是一种权衡。一方面,您对存储大小有要求,这会对性能产生普遍影响(更大的密钥 -> 更大的尺寸 -> 更多的 IO,并且 IO 带宽可能是您拥有的最稀缺的资源)。另一方面,以节省空间的名义选择错误的聚集键可能会产生查询性能后果,通常比宽键导致的问题更糟糕。
至于主键的选择,它甚至不应该是一个问题:您的数据模型、您的应用程序逻辑应该决定主键是什么。
话虽这么说,我的2C型:NVARCHAR(20)
是不是宽。是完全可以接受的聚集键大小,即使对于大表也是如此。
小智 3
除了在不知道的情况下使用 nvarchar/varchar 时使用宽键的风险之外,不应该有任何固有/明显的惩罚。特别是当您开始将它们组合成复合键时。
但在你的 (20) 长度的例子中,你应该没问题,我不会太担心。因为如果 CODE 是您主要查询数据的方式 - 聚集索引听起来非常明智。
但是,您应该考虑您是否真的希望将其作为主键或只是唯一(聚集)索引。聚集索引和主键之间存在(小)差异(基本上 - 主键标识您的数据,但索引是您查询数据的方式),因此,如果您愿意,您可以轻松地将 ID_Code 作为主键,通过 CODE 创建唯一的聚集索引。(注意:SQL Server会自动将你的主键变成聚集索引,除非你自己手动创建了聚集索引)
还要考虑现在您是否确实需要 ID_Code,因为您已经有了唯一的 CODE。