我是否应该仅出于PK目的将自动递增/ IDENTITY字段添加到交叉引用表中?


9

我将以下交叉引用表添加到我的SQL Server托管的数据库中:

company_id bigint not null (FK)
org_path nvarchar (2048) not null

company_id字段引用id另一个表(其中是主键)中的字段。

假设还可以有多个具有相同记录的记录,则company_id任何主键都必须使用两个字段。但是,由于org_pathSQL Server太长,因此无法使用这两个字段创建密钥。

至于org_path,这是它存在的唯一表。对该表的查询极有可能会询问所有条目或的所有org_path条目company_id。或者换种说法,该表是否会被查询似乎令人怀疑org_path。此外,它不太可能org_path会被更新,更不可能被插入并且可能很少被删除。

我希望总行数可以低到几千。

另外,这nvarchar (2048)是因为该值必须模仿第三方数据库中的值。一个典型的例子是

\Translation Providers\[customer name]\[order name]\

并可能包含变音符号。

所以我的问题是这样的:添加自动递增id字段并将其与company_id主键结合使用会更有效,还是会增加不必要的开销-并且company_id作为另一个表中主键的事实是否具有任何意义?效果在这里?

Answers:


7

对于comany_id单独存在的非唯一聚集索引,SQL Server将自动向所有重复的聚集索引键(即键值的第二个和后续键)添加4字节整数唯一符,以使其唯一。但是,这不会向用户公开。

将您自己的唯一标识符添加为辅助键列的优点是,您仍然可以继续搜索,company_id但也可以更有效地查找单个行(使用company_id, identitycol而不是company_id使用残留谓词on org_path)。这样,聚集索引在上将是唯一的company_id, identitycol,因此不会添加任何隐藏的唯一标识符。

另外,如果您最终获得的重复(company_id,org_path),则拥有显式的身份列(一种“公开的唯一标识符”)将使定位其中一个进行删除或更新变得更加容易。


12

要考虑的一件事是主键和聚簇索引不是同一件事。主键是一个约束,它处理数据生存所依据的规则(即数据完整性);它与效率/性能无关。主键要求键列是唯一的(组合)和NOT NULL(单独)。PK是通过唯一索引强制执行的,尽管它可以是群集的也可以是非群集的。

聚集索引是一种物理(即在磁盘上)对表中的数据进行排序并处理性能的方法。它与数据完整性无关。聚集索引可以要求键列是唯一的(组合),但不是必须的。但是,由于聚簇索引是数据的物理顺序,因此无论如何都需要唯一地标识每一行。因此,如果您不将其设置为要求唯一性,它将通过一个隐藏的4字节“ uniquifier”列创建自己的唯一性。该列始终在非唯一聚集索引中存在,但是当键字段是唯一的(组合)时,它不会占用任何空间。首先了解一下“ uniquifier”列的工作方式(在聚集索引中以及对非聚集索引的影响),用于测试Uniquifier大小的T-SQL脚本

因此,主要问题是:

添加自动递增id字段并将其与company_id主键结合使用会更有效,还是会增加不必要的开销

将这两个概念混为一谈,因此尽管确实存在一些重叠,但仍需要分别解决。

应该IDENTITY添加一列还是不必要的开销?

如果添加一INT IDENTITY列并使用它来创建PK(假设它是集群PK),则会向每行添加4个字节。该列是可见的,可用于查询。它可以作为外键添加到其他表中,尽管在这种情况下不会发生。

如果不添加该INT IDENTITY列,则无法在此表上创建PK。但是,只要不使用该UNIQUE选项,仍可以在表上创建聚簇索引。在这种情况下,SQL Server将添加一个名为“ uniquifier”的隐藏列,其行为如上所述。由于该列是隐藏的,因此无法在查询中使用或用作外键的引用。

因此,就效率而言,这些选择大致相同。是的,将有稍微由具有非唯一聚集索引占据更少的空间,由于某些行(连同初始唯一密钥值的)占0字节,而在所有的行IDENTITY/ PK将采取4个字节。但是不会有足够的0字节行(尤其是预期的行数很少)来注意到差异,更不用说能够ID在查询中使用该列的便利了。

INT IDENTITY列还是org_path持久计算列的哈希?

鉴于您不会根据org_path值查找行,因此添加“持久计算列”的开销以及需要在查询中计算该哈希值以与“计算列”进行匹配都没有道理(这是我的原始建议,可在此处的修订历史中找到,该建议基于问题的初始措辞/细节)。在这种情况下,INT IDENTITY“ ID”列可能是最好的。

关键列顺序

鉴于IDColumn很少(如果有的话)用在查询中,并且鉴于两个主要用例是获取“所有行”或“给定的所有行company_id”,我将在上创建PK company_id, id。并且由于这意味着行不会被顺序插入,因此我将指定a FILLFACTOR为90。您还需要确保进行常规索引维护以减少碎片。

第二个问题

company_id是另一个表中的主键的事实在这里是否有任何作用

没有。

触发

由于中的org_pathcompany_id是唯一的,因此您仍应创建一个Trigger INSERT, UPDATE来强制执行此操作。在触发器中,对IF EXISTS可能执行COUNT(*)和的查询执行GROUP BY company_id, org_path。如果发现任何问题,请发出a ROLLBACK来取消DML操作,然后RAISERROR说有重复项。

校对

在我的最初答案中(基于问题的原始措辞/稀疏细节,并在此处的修订历史记录中提供),我建议可能使用二进制(即_BIN2)归类。现在我们已经了解了到底org_path是什么,我建议使用二进制归类。由于会有变音符号,因此您确实想使用语言对等。



0

为什么需要PK?

为什么不只将company_id用作非聚集索引?

您说搜索最多的是所有条目或按company_id搜索
很少更新
很少删除
org_path,这是它存在的唯一表

马丁·史密斯(Martin Smith)的答案可能会为您提供所需的信息,
我不熟悉自动添加4字节整数唯一符的过程。
也许我丢失了某些内容,但是如果您没有为其他列建立索引,那么在这种使用情况下,我认为没有任何用处

如果您担心DRI,则表应使用Company表作为company_id的FK


嘿。关于“ 为什么不只将company_id用作非聚集索引? ”:因为这样做有2个缺点:1)它将多占用1个空间,而聚集索引表,因此没有其他项目,并且2)除非那是一INCLUDE列,否则仍然需要RID查找才能获得NVARCHAR字段,但这甚至更糟,因为它只是复制表。没错,PK不是必需的。重要的部分是聚集索引。但是一旦有了身份,不妨选择PK。并请查看我的答案中的新链接,以获取有关Uniquifier😃的逐步介绍
Solomon

@srutzky但是它避免了4字节整数唯一符,所以我认为这是一种洗礼
狗仔队

如果行数少于1万,那就没关系了;您可能需要进入数百万行,然后才能注意到仅4个字节的影响。因此,对于“获取所有行”查询,这些选项实际上没有任何区别。但是对于“为company_id = @param获取”查询,由company_id对数据进行物理排序将很有帮助,尤其是当它不需要对每一行进行RID查找时。
所罗门·鲁兹基

@srutzky洗是洗-10K或1G。这只是OP需要考虑的事情。
狗仔队
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.