Stu*_*ler 188 sql-server primary-key
在为我之前问的另一个问题创建测试数据库时,我记得可以声明主键 NONCLUSTERED
什么时候使用NONCLUSTERED主键而不是CLUSTERED主键?
提前致谢
Rem*_*anu 205
问题不是“PK 何时应该是 NC”,而是您应该问“聚簇索引的正确键是什么”?
答案实际上取决于您如何查询数据。聚集索引优于所有其他索引:因为它总是包含所有列,所以总是覆盖。因此,可以利用聚集索引的查询当然不需要使用查找来满足某些投影列和/或谓词。
另一个难题是如何使用索引?典型的模式有以下三种:
因此,如果您分析预期负载(查询)并发现大量查询将使用特定索引,因为它们使用某种受益于索引的访问模式,建议将该索引作为聚集索引是有意义的。
另一个因素是聚簇索引键是所有非聚簇索引使用的查找键,因此宽聚簇索引键会产生连锁反应并加宽所有非聚簇索引,宽索引意味着更多页面,更多 I/O ,更多的记忆,更少的善良。
一个好的聚集索引是稳定的,它在实体的生命周期内不会改变,因为聚集索引键值的改变意味着该行必须被删除和插入。
一个好的聚簇索引不是随机增长的(每个新插入的键值都大于前面的值),以避免页面分裂和碎片(不会与FILLFACTORs混淆)。
既然我们知道了什么是好的聚集索引键,那么主键(这是一个数据建模逻辑属性)是否符合要求?如果是,那么应该对 PK 进行聚类。如果不是,那么 PK 应该是非聚集的。
举个例子,考虑一个销售事实表。每个条目都有一个 ID 作为主键。但是绝大多数查询要求一个日期和另一个日期之间的数据,因此最好的聚集索引键是销售日期,而不是ID。具有与主键不同的聚集索引的另一个示例是选择性非常低的键,例如“类别”或“状态”,即只有很少不同值的键。将具有这种低选择性键的聚集索引键作为最左边的键,例如(state, id),通常是有意义的,因为范围扫描会查找特定“状态”中的所有条目。
关于堆上非聚集主键的可能性的最后一个注意事项(即根本没有聚集索引)。这可能是一个有效的场景,典型的原因是当批量插入性能至关重要时,因为与聚集索引相比,堆具有明显更好的批量插入吞吐量。
Ben*_*cka 27
维基百科上说明了使用聚集索引的基本原因:
聚类将数据块更改为特定的不同顺序以匹配索引,从而使行数据按顺序存储。因此,在给定的数据库表上只能创建一个聚集索引。聚集索引可以大大提高检索的整体速度,但通常只有在以与聚集索引相同或相反的顺序顺序访问数据时,或者选择了一系列项目时。
假设我有一张 People 表,这些人有一个 Country 列和一个唯一的主键。这是一个人口统计表,所以这些是我唯一关心的事情;哪个国家以及有多少独特的人与该国家有关。
因此,我只可能在 Country 列中选择 WHERE 或 ORDER;主键上的聚集索引对我没有任何好处,我不是通过 PK 访问这些数据,而是通过另一个列访问它。因为我只能在一张表上有一个聚集索引,所以将我的 PK 声明为 Clustered 会阻止我在 Country 上使用聚集索引。
此外,这里有一篇关于Clustered vs Nonclustered Indexes的好文章,结果表明聚集索引导致 SQL Server 6.5 中的插入性能问题(至少希望与我们这里的大多数人无关)。
如果您将聚集索引放在 IDENTITY 列上,那么您的所有插入都将发生在表的最后一页 - 并且该页在每个 IDENTITY 的持续时间内都被锁定。没什么大不了的……除非你有 5000 人都想要最后一页。那么你对该页面有很多争用
请注意,在以后的版本中情况并非如此。
一个很常见的例子:
Customer与CustomerID作为表CLUSTERED PRIMARY KEYOrderID (PK), CustomerID, OrderDate和其他一些列的订单表OrderPositions 和 OrderPositionID (PK), OrderId, ProductID, Amount, Price ...当然,“视情况而定”是 - 几乎总是 - 正确的答案,但大多数应用程序(不是 BI-Reports)将基于客户工作(例如,您以客户 278 的身份登录网站并单击“我的订单”或店员列出客户 4569 的所有订单,否则您的发票例程将汇总客户 137 的所有订单)。
在这种情况下,通过OrderID. 是的,您将有SELECT ... WHERE OrderId = ?关于列出订单详细信息的查询,但这通常是简短且廉价的(3 次读取)索引搜索。
另一方面,如果您将Order通过 对表进行聚类CustomerID,则每次查询表时就不必进行多次键查找CustomerId = ?。
本CLUSTERED INDEX应该是永远UNIQUE的,否则的SQL Server将添加一个不可见的(=不可用)INT列UNIQUIFIER,以确保uniquiness -它将使更多的意义,以增加真正的(可用)数据,那么一些随机(取决于插入顺序)的东西。
因为客户(希望)会下多个订单,所以我们必须添加OrderID或(如果您通常为此排序)OrderDate(如果是日期时间 - 否则客户每天只能下一个订单)到在CLUSTERED INDEX和结束:
CREATE UNIQUE CLUSTERED INDEX IX_Orders_UQ on Orders (CustomerID, OrderID)
相同的规则适用于OrderPositions表。通常大多数查询会列出特定订单的所有头寸,因此您应该使用OrderPositionIDasNONCLUSTERED和 a UNIQUE CLUSTERED INDEXon创建 PK OrderId, OrderPositionID。
顺便说一句:Customer表按其 PK 聚类是正确的(CustomerID,因为它是“顶级表”并且 - 在典型应用程序中 - 主要由其 CustomerID 查询。
纯查找表作为 eg Gendersor InvoiceTypesorPaymentType是应该按其 PK 聚类的表的另一个示例(因为您通常会在GenderId, InvoiceTypeIdor上加入它们PaymentTypeId)。