NVARCHAR列作为PRIMARY KEY或UNIQUE列


11

我正在开发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
    )
)

但是现在我想使用[CODE]列作为主键并删除[ID_CODE]列。

如果我的NVARCHAR专栏为,是否有任何问题或惩罚PRIMARY KEY

[CODE]列值必须是唯一的,因此我认为可以UNIQUE为该列设置约束。

我必须用[CODE]作主键还是UNIQUE[CODE]列设置约束会更好?


1
考虑的最重要的事情是表中将有多少行?
詹姆斯·Z

本身并不是一个答案,但我倾向于认为您的CODE专栏应该是唯一的,而不是主键。我怀疑它带有信息。如果该信息以任何方式可更改,则您CODE应该更改或已过时。那会使您的主键变得不稳定,而我看不到效果很好。最好让您的PK只是一个密钥,并且您的CODE可以完成您喜欢的事情。只是一个意见。
曼戈

@Manngo,感谢您的评论。是的,我是这样做的:ID_CODE是主键,而CODE是UNIQUE。
VansFannel

Answers:


13

是的,对于主键使用字符串而不是数字类型绝对会带来负面影响,并且如果PK是集群的(在您的情况下确实如此),甚至会带来更多负面影响。但是,您看到使用字符串字段的效果的程度取决于以下情况:a)此表中有多少行,b)其他表中有多少行被此PK外键了。如果您在此表中只有1万行,而在其他一些表中只有10万行,那么您可以通过该字段将该表FK链接到该表,那么也许不会那么引人注意。但是随着行数的增加,这些影响肯定会变得更加明显。

您需要考虑将聚集索引中的字段转移到非聚集索引中。因此,您不仅要查看每行最多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]作为主键是最好的选择吗?

这一切都取决于很多因素,我已经提到了其中一些因素,但将重申一下:

主键是识别单个行的方式,无论是否被任何外键引用。您的系统如何在内部标识该行与(或不一定)与用户如何标识自己/该行相关。任何具有唯一数据的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。


匿名的拒绝投票者(似乎也拒绝投票@noIDonthissystem的答案)是否愿意提出任何建设性的批评或指出一些有缺陷的逻辑?
所罗门·鲁兹基

感谢您的回答。是[ID_CODE],因为PRIMARY KEY,如果我使用的最佳选择[CODE]列以查找表?
VansFannel 2015年

@VansFannel请参阅我的更新。谢谢。
所罗门·鲁兹基

我加入了这个dba社区来支持这个答案。
艾哈迈德·阿尔斯兰

6

您必须将概念分开:

  • 主键是一个设计概念,是表中各项的逻辑属性。它在表条目的生存期内应该是不变的,并且应该是应用程序中用来引用该条目的键。

  • 聚集索引是一种存储概念,一种物理属性。它应该是查询的最常见访问路径,它应该满足大多数情况下的覆盖索引要求,并满足尽可能多的范围查询。

主键不需要是聚集索引。您可以具有ID_CODEPK和(CODE_LEVEL, CODE)聚簇键。或相反。

较大的聚集键具有一些负面影响,因为较大的键意味着索引页上的密度较低,而所有非聚集索引上的消耗量较大。例如,已经有数吨的墨水溅到这个话题上。从对聚簇键的更多考虑开始–聚簇索引的争论还在继续!

但是,要点是,聚簇索引键的选择主要是权衡取舍。一方面,您对存储大小有要求,并对性能有普遍影响(较大的键->较大的尺寸->更多的IO,并且IO带宽可能您最稀缺的资源)。另一方面,以节省空间的名义选择错误的集群键可能会对查询性能造成影响,通常比宽键导致的问题更糟糕。

至于主键的选择,这甚至不应该成为问题:数据模型,应用程序逻辑应决定主键是什么。

话虽这么说,我的2C型:NVARCHAR(20)不是宽。即使对于大型表,也是完全可以接受的集群键大小。


感谢您的回答。是[ID_CODE],因为PRIMARY KEY,最好的选择,如果我使用[CODE]的列(也许[CODE_LEVEL])来查找表?
VansFannel 2015年

@VansFannel只有可以回答。
Remus Rusanu

但您认为...
VansFannel 2015年

2
我认为必须考虑整个表和所有索引的确切DDL,引用它的外键,估计的行数,预期的查询工作量,应用程序预期的SLA,至少要考虑可用于硬件和许可的资源。
Remus Rusanu 2015年

谢谢。我将使用[CODE]列作为PRIMARY KEY。
VansFannel 2015年

4

我绝不允许任何人nvarchar(20)在我的数据库中将PK做为PK。您浪费磁盘空间和缓存内存。该表上的每个索引及其所有FK都复制此宽值。如果他们可以证明它是一个char(20)。您要存储哪种数据CODE?您是否真的需要存储nvarchar字符?我倾向于将PK设置为用户看不到的“内部”值,并且尝试将显示的值分开。有时需要更改显示的值,这对于PK + FK来说非常成问题。

另外,您是否意识到“ bigint身份(1,1)”最多可以增加9,223,372,036,854,775,807?

[ID_CODE] [bigint] IDENTITY(1,1)

除非您要为Google建立该数据库,否则正常的数据库int identity (1,1)上限不能超过20亿个吗?


int在SQL中为4字节,为-21亿至+ 21亿。
datagod

@datagod,哈哈,谢谢,我数错了!
该系统上没有ID

感谢您的回答。是[ID_CODE],因为PRIMARY KEY,如果我使用的最佳选择[CODE]列以查找表?谢谢。
VansFannel

以前我一直在这条船上,直到有人使用“ int”的顺序性质来预测数据库中的数据/用户并收获了我拥有的大部分东西。再也不。面向公众的数据库需要更多些信息才能获取。
DaBlue

3

除了在不知情的情况下使用nvarchar / varchar时使用宽键的风险之外,应该没有内在的/明显的损失。特别是如果您开始将它们组合为复合键。

但是在您的长度为(20)的示例中,您应该可以,而且我也不必为此担心。因为如果CODE是您主要查询数据的方式-听起来的聚集索引非常明智。

但是,您应该考虑是将其作为主键还是仅作为唯一(聚集)索引。聚集索引和主键之间有一个(很小的)差异(基本上-主键标识您的数据,但是索引是您查询数据的方式),因此,如果您希望可以轻松地将ID_Code用作主键,并且在CODE上创建唯一的聚集索引。(注意:除非您自己手动创建了聚集索引,否则 SQL Server会自动将主键设置为聚集索引)

另外,现在考虑拥有唯一的CODE时,是否还需要ID_Code。


2
实际上,最大大小NVARCHAR(20)40个字节,并且由于它是可变长度的列,因此对于聚集索引而言,它并不是最佳选择。ID_CODE作为一个BIGINT IDENTITY将是更好的在这里的选择!
marc_s 2015年

我知道它是40字节,但是没有太多理由指定它,因为它离900字节还差得远。如果你主要是从查询码中的数据这将是一个更好的选择,以避免冗余索引维护,因为你还是会需要它的索引,然后你必须通过聚集aftwards查找
阿伦S. Hansen

值得一提的是-我忘记提及了,我怀疑@marc_s正在解决的问题是,这种类型的索引比顺序标识可能导致更大的索引碎片,但是在这种基于特定情况的基础上,我仍然将其视为明智的索引在查询因素上。
艾伦·汉森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.