内部联接的索引与where子句

时间:2010-01-04 21:21:21

标签: sql-server indexing join

如果我有以下查询:

select some cols 
   from tbl_a
INNER JOIN tbl_b ON tbl_a.orderNumber = tbl_b.orderNumber
   where tlb_b.status = 'XX'

假设两个表只有订单号上的聚簇索引,从性能角度来看,扩展表b上的聚簇索引以包含where子句中引用的状态列会更好吗?

7 个答案:

答案 0 :(得分:7)

  1. 您扩展tbl_b以在 orderNumber:create clustered index ... on tbl_b(orderNumber, status)之后添加状态。对于上面的查询,没有明显的区别。该计划仍然必须端到端扫描tbl_b并匹配tbl_a中的每个订单号(可能是合并连接)。

  2. 您可以扩展tbl_b以在 orderNumber:create clustered index ... on tbl_b (status, orderNumber)之前添加状态。现在有一个巨大的差异。该计划可以在tbl_b上进行范围扫描,仅使用状态'xx'进行范围扫描,并且只使用嵌套循环连接匹配对应orderNumber的tbl_a。

  3. 放置一个低选择性列(通常是'status')作为索引中最左边的键通常是一件好事。在“聚集索引”中最左边的列创建“状态”行通常也是一件好事,因为它将具有相同状态的记录物理地组合在一起。请注意,这样做会对所有查询产生影响。如果没有指定status,你也会失去orderNumber的直接访问权限,你必须单独在orderNumber上添加一个非聚集索引来覆盖它(通常是PK非聚集索引)。

    我在知道您的实际数据基数和选择性的情况下做了所有这些评论。如果tbl_a和tbl_b的基数非常偏差,那么事情可能会有所不同。例如。如果tbl_a有10个记录,其中包含10个不同的订单号,而tbl_b有10M记录,其中10M订单号超过我的建议,则选项2将没什么区别,因为该计划将始终选择tbl_a扫描tbl_b中的搜索范围查找10次。

答案 1 :(得分:2)

是的,很可能。这称为覆盖指数。可以从索引提供整个查询,而无需访问tbl_b。

但是,您应该考虑其他查询的性能影响,尤其是那些更新状态列的查询。

答案 2 :(得分:2)

将状态添加到聚簇索引将允许SQL Server更有效地解析where子句。 SQL Server可以首先从索引中查找特定状态的所有订单,并基于此执行连接。要使其工作,状态必须是索引中的第一列:

(status, orderNumber)

请注意,如果以这种方式扩展主键,则orderNumber列不再保证是唯一的。因此最好将其添加为单独的索引。

单独索引的有用程度取决于状态的选择性。如果您正在搜索“失败”并且只有1%的订单具有该状态,则索引将非常有用。如果状态不是很有选择性,SQL Server甚至可能根本不使用新索引。

答案 3 :(得分:2)

我不会改变表的主键以包含辅助列...最好只将新的非聚集索引添加到状态字段。

原因是聚簇索引表示磁盘上数据的物理顺序。如果添加复合列,则在添加订单或更新状态时,表(在某些情况下)将需要在磁盘上重新排序。由于IO和锁定时间增加,这非常昂贵。

答案 4 :(得分:1)

是的,我相信会更好。您可以确定的一种方法是在描述时扩展主键,并查看此查询的查询计划。如果您没有看到扫描完成,则表示正在使用主键中的额外列。

答案 5 :(得分:1)

将类似状态的非顺序字段添加到聚簇索引会降低写入速度。您需要确定写入时的性能是否比读取时的性能提升更有价值。

还可以选择在(ordernumber,status)上创建第二个索引。通过在(status,ordernumber)上创建索引,您可能会受益更多。

答案 6 :(得分:1)

MS documentation建议:

  

...使用尽可能少的列创建聚簇索引。如果定义了大型聚簇索引键,则在同一个表上定义的任何非聚簇索引都将显着增大,因为非聚簇索引条目包含聚簇键。

基于此,我不会将状态列添加到聚簇索引,并创建一个单独的非聚集索引,如果还有其他列需要考虑,它可能是覆盖索引。