awj*_*awj 9 sql-server primary-key clustered-index t-sql
我将以下交叉引用表添加到我的 SQL Server 托管的数据库中:
company_id bigint not null (FK)
org_path nvarchar (2048) not null
Run Code Online (Sandbox Code Playgroud)
该company_id
字段是指id
另一个表中的字段(它是主键)。
鉴于也可以有多条记录具有相同的company_id
,任何主键都必须使用这两个字段。但是,我无法使用这两个字段创建密钥,因为org_path
对于 SQL Server 来说太长了。
至于org_path
,这是它存在的唯一表。对该表的查询很可能会要求所有条目或所有org_path
条目company_id
。或者换句话说,这个表是否会被 查询,这看起来令人怀疑org_path
。此外,不太可能org_path
会更新,更有可能插入和 - 可能很少 - 删除。
我预计总行数将在数千个以内。
此外,这nvarchar (2048)
是因为该值必须模仿第三方数据库中的值。一个典型的例子是这样的
\Translation Providers\[customer name]\[order name]\
Run Code Online (Sandbox Code Playgroud)
并且可以包含变音符号。
所以我的问题是:添加一个自动增量id
字段并将其与company_id
主键结合使用会更有效,还是会增加不必要的开销 -company_id
另一个表中的主键这一事实是否有任何影响?效果在这里?
Sol*_*zky 12
需要考虑的一件事是主键和聚集索引不是一回事。主键是一个约束,处理数据生存的规则(即数据完整性);它与效率/性能无关。主键要求键列是唯一的(组合)和 NOT NULL(单独)。PK 通过唯一索引强制执行,但它可以是聚集的或非聚集的。
聚集索引是一种物理(即在磁盘上)对表中的数据进行排序并处理性能的方法;它与数据完整性无关。聚集索引可以要求键列是唯一的(组合),但它不需要。但是,由于聚集索引是数据的物理顺序,因此无论如何都需要唯一标识每一行。因此,如果您不将其设置为需要唯一性,它将通过一个隐藏的 4 字节“uniquifier”列创建自己的唯一性。该列始终存在于非唯一聚集索引中,但当键字段唯一(组合)时,它不占用任何空间。要直接了解这个“uniquifier”列的工作原理(在聚集索引中和对非聚集索引的影响),请查看我发布在 PasteBin 上的这个测试脚本:T-SQL script to test Uniquifier size。
因此,主要问题是:
添加自动增量
id
字段并将其用company_id
作主键会更有效,还是会增加不必要的开销
将这两个概念混为一谈,因此需要分别处理它们,尽管肯定存在一些重叠。
应该IDENTITY
添加一列还是不必要的开销?
如果您添加一INT IDENTITY
列并使用它来创建一个 PK,假设它是一个集群 PK,那么每行会增加 4 个字节。此列在查询中可见并可使用。它可以作为外键添加到其他表中,但在这种特殊情况下不会发生。
如果不添加INT IDENTITY
列,则无法在此表上创建 PK。但是,只要不使用该UNIQUE
选项,您仍然可以在表上创建聚集索引。在这种情况下,SQL Server 将添加一个名为“uniquifier”的隐藏列,其行为如上所述。因为该列是隐藏的,所以它不能用于查询或作为外键的参考。
因此,就效率而言,这些选项大致相同。是的,由于某些行(具有初始唯一键值的行)占用 0 字节,而/ PK中的所有行都占用 4 个字节,因此非唯一聚集索引占用的空间会稍微减少IDENTITY
。但是没有足够的 0 字节行(尤其是预期的行数很少)会注意到差异,更不用说超过能够ID
在查询中使用该列的便利性了。
INT IDENTITY 列还是org_path
持久计算列的哈希?
鉴于您不会根据org_path
值查找行,那么添加持久计算列的开销以及需要在查询中计算该哈希以匹配计算列是没有意义的(这是我的原始建议,可在此处的修订历史记录中获得,该建议基于问题的初始措辞/细节)。在这种特殊情况下,INT IDENTITY
“ID”列可能是最好的。
键列顺序
鉴于ID
Column 很少(如果有的话)在查询中使用,并且考虑到两个主要用例是获取“所有行”或“给定的所有行company_id
”,我将在company_id, id
. 因为这意味着行不是按顺序插入的,所以我将指定 aFILLFACTOR
为 90。您还需要确保定期进行索引维护以减少碎片。
第二个问题
company_id 是另一个表中的主键这一事实在这里有任何影响吗
不。
扳机
由于org_path
acompany_id
中的值是唯一的,您仍然应该创建一个 Trigger onINSERT, UPDATE
来强制执行此操作。在 Trigger 中,执行一个IF EXISTS
可能执行 aCOUNT(*)
和的查询GROUP BY company_id, org_path
。如果发现任何东西,发出一个ROLLBACK
取消 DML 操作,然后RAISERROR
说有重复项。
整理
在我最初的回答中(基于问题的原始措辞/稀疏细节,并在此处的修订历史记录中可用),我建议可能使用二进制(即_BIN2
)排序规则。现在我们已经深入了解到底org_path
是什么,我不建议使用二进制排序规则。由于会有变音符号,因此您确实希望使用语言对等。
对于comany_id
单独的非唯一聚集索引,SQL Server 将自动向所有重复(即键值的第二个和后续)聚集索引键添加一个 4 字节整数唯一标识符,以使其唯一。但是,这不会向用户公开。
添加您自己的唯一标识符作为辅助键列的优点是,您仍然可以通过company_id
搜索更有效地搜索单个行(使用company_id, identitycol
而不是company_id
使用残差谓词 on org_path
)。然后聚集索引在 上将是唯一的company_id, identitycol
,因此不会添加隐藏的唯一标识符。
此外,如果您最终得到重复的 for (company_id,org_path)
,则具有显式标识列(一种“公开的唯一标识符”)将更容易仅针对其中一个进行删除或更新。
归档时间: |
|
查看次数: |
1510 次 |
最近记录: |