Answers:
问题不是“ PK何时应为NC”,而是您应该问“聚集索引的正确密钥是什么”?
答案实际上取决于您如何查询数据。聚集索引比所有其他索引都有一个优势:因为它总是包含所有列,所以总是被覆盖。因此,可以利用聚集索引的查询当然不需要使用查找来满足某些预计的列和/或谓词。
另一个难题是如何使用索引?有三种典型的模式:
因此,如果您分析了预期的负载(查询),并发现大量查询将使用特定索引,因为它们使用了受益于索引的某种访问模式,则建议将该索引建议为聚簇索引。
另一个因素是,聚集索引键是所有非聚集索引使用的查找键,因此,较宽的聚集索引键会产生连锁反应,并扩大所有非聚集索引,并且较宽的索引意味着更多的页面,更多的I / O ,更多的记忆,更少的善良。
一个好的聚簇索引是稳定的,它在实体的生存期内不会改变,因为聚簇索引键值的更改意味着必须删除该行并将其插入回去。
一个好的聚簇索引的增长顺序不是随机的(每个新插入的键值都大于前一个值),以避免页面拆分和碎片化(不会与FILLFACTOR
s 混为一谈)。
因此,既然我们知道什么是好的聚集索引键,那么主键(这是数据建模逻辑属性)是否符合要求?如果是,则应将PK聚类。如果否,则PK应该是非群集的。
举一个例子,考虑一个销售事实表。每个条目都有一个ID作为主键。但是绝大多数查询都要求一个日期和另一个日期之间的数据,因此最佳的聚集索引键将是销售日期,而不是ID。具有与主键不同的聚簇索引的另一个示例是选择性很低的键,例如“类别”或“状态”,即只有很少的不同值的键。例如(state, id)
,将具有低选择性键的聚簇索引键作为最左键通常是有意义的,因为范围扫描会查找特定“状态”中的所有条目。
最后一点关于在堆上使用非聚集主键的可能性(即根本没有聚集索引)。这可能是一个有效的场景,典型的原因是批量插入性能至关重要时,因为与聚簇索引相比,堆具有明显更好的批量插入吞吐量。
(state, id)
。在此示例中,将无法满足“良好的聚簇索引不按顺序增长”的要求,不是吗?那么我们可以将其视为良好的聚集索引吗?
使用聚集索引的基本原因已在Wikipedia上阐明:
聚类将数据块更改为某个不同的顺序以匹配索引,从而导致按顺序存储行数据。因此,在给定的数据库表上只能创建一个聚簇索引。聚簇索引可以极大地提高整体检索速度,但通常仅在按聚簇索引的相同或相反顺序顺序访问数据时,或者在选择一系列项目时才如此。
假设我有一个人的表,这些人有一个国家列和一个唯一的主键。这是一个人口统计表,所以这是我唯一关心的事情;哪个国家/地区以及与该国家/地区有多少不重复的人。
因此,我只能选择“国家/地区”列中的“选择位置”或“按顺序”;主键上的聚集索引对我没有任何帮助,我没有通过PK访问此数据,而是通过另一列访问了它。由于我只能在一个表上有一个聚集索引,因此将我的PK声明为“聚集”将使我无法在“国家/地区”上使用聚集索引。
另外,这是一篇有关聚集索引和非聚集索引的好文章,结果证明聚集索引导致SQL Server 6.5中的插入性能问题(至少希望与我们大多数人无关)。
如果将聚集索引放在IDENTITY列上,则所有插入都将在表的最后一页上发生-并且该页面在每个IDENTITY期间都被锁定。没什么大不了的……除非您有5000个人都想要最后一页。然后您对该页面有很多争论
请注意,在更高版本中不是这种情况。
如果您的主键是UNIQUEIDENTIFIER
,请确保指定它为NONCLUSTERED
。如果将其群集,则每个插入都必须进行一堆记录改组以将新行插入正确的位置。这将提高坦克的表现。
UNIQUEIDENTIFIER
类型也存在,并且具有生成唯一密钥的相同可能性,尽管它仍然遭受128大小的影响。
一个非常常见的示例:
Customer
带有CustomerID
as的表CLUSTERED PRIMARY KEY
OrderID (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
以确保唯一性-添加真实(可用)数据比添加一些随机(取决于插入顺序)的东西更有意义。
因为客户将(希望)下达多个订单,所以我们必须添加OrderID
或(如果您通常对此进行排序)OrderDate
(如果是日期时间-否则客户每天将限于一个订单)在CLUSTERED INDEX
和结束:
CREATE UNIQUE CLUSTERED INDEX IX_Orders_UQ on Orders (CustomerID, OrderID)
相同的规则适用于OrderPositions
表。通常,大多数查询会列出特定顺序的所有头寸,因此您应使用OrderPositionID
as NONCLUSTERED
和UNIQUE CLUSTERED INDEX
on 来创建PK OrderId, OrderPositionID
。
顺便说一句:正确的是,Customer
表是由其PK聚集的(CustomerID
,因为它是“顶级表”,并且在典型的应用程序中,大多数情况下将由其CustomerID查询。
纯查找表作为例如Genders
或InvoiceTypes
或者PaymentType
是应该由它来PK集群(因为你通常会加入他们的表上的另一个例子GenderId
,InvoiceTypeId
或PaymentTypeId
)。