2010-01-04 52 views
3

如果我有以下查询:指标的内部与联接WHERE子句

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

假设两个表有唯一的订单号聚簇索引,这将是从性能的角度更好地延长表的聚集索引b包含where子句中引用的状态列?

+0

这取决于。主要(或唯一)约束?如果不是,每个连接列的基数[单个值的出现频率]是多少?列tbl_b.status的基数是多少? ...是的,似乎有点显而易见,但我决定不对这个十年的其他人的数据做出假设。 – 2010-01-04 21:55:53

回答

6
  1. 您延长tbl_b的订单号码后添加状态create clustered index ... on tbl_b(orderNumber, status)。对于上面的查询,不会有明显的差异。该计划仍然必须首先扫描tbl_b,并匹配tbl_a中的每个订单号(可能是合并连接)。

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

将低选择性列(如'status'通常是)作为索引中最左边的键通常是件好事。并且在“聚集索引”的最左边的列中创建一个类似'状态'的行通常也是一件好事,因为它将物理上具有相同状态的记录分组在一起。请注意,这样做会对所有查询产生影响。如果未指定状态,则还会丢失orderNumber的直接访问权限,因此您必须在orderNumber中添加一个非聚集索引来覆盖该索引(这通常是PK非聚集索引)。

我做了所有这些评论,不知道你的实际数据基数和选择性。如果tbl_a和tbl_b的基数非常歪斜,那么事情可能会有所不同。例如。如果tbl_a有10条记录和10个不同的订单号,而tbl_b有10M条订单号的记录比我的建议还要多,则选项2几乎没什么区别,因为计划总是选择对tbl_b进行10次寻找范围查找的扫描。

+0

感谢您的回答。如果我仅创建聚集索引orderNumber,是否可以添加仅具有状态而非状态orderNumber的非聚集索引(因为聚集索引包含在非聚集索引中)? – SuperCoolMoss 2010-01-05 08:59:45

+0

(状态)上的非聚集索引几乎没有用处。您应该在(status,orderNumber)imho上创建非聚集索引。 – 2010-01-05 16:26:50

1

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

2

是的,很可能。这被称为覆盖指数。整个查询可以从索引提供,根本不访问tbl_b。

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

+0

一个覆盖指数也会包括“一些cols” – Andomar 2010-01-04 21:27:24

+1

此外,一个聚集索引是通过定义覆盖指数:) – Andomar 2010-01-04 21:33:04

+0

@Andomar(第二评论) - 不,我不这么认为。 “覆盖”索引仅覆盖特定查询的上下文,因为索引“覆盖”查询中使用的表中的所有列。 – 2010-01-05 08:09:52

1

将非顺序字段状态添加到聚集索引中会减慢写入速度。您需要确定写入的性能是否比读取的性能增加更有价值。

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

2

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

(status, orderNumber) 

请注意,如果你以这种方式延伸的主键,把orderNumber柱不再guarantueed是唯一的。所以最好把它作为一个单独的索引来添加。

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

+0

+1只是一个简短的说明,即使在非常低的选择性下,仍然会使用最左边位置的状态。即使只有两个可能的状态值(比如0,1),那么tbl_b上的索引(status,orderNumber)仍然会将候选订单数减少一半,因此该计划很可能会选择它。我故意忽略了'一些列'的影响(即投影列表的可投影性),因为我认为这是一个不同的主题。 – 2010-01-04 22:12:40

1

MS documentation建议:

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

基于这一点,我也不会在状态栏添加到聚集索引,并创建一个独立的,非聚集索引,可能是如果有其他列考虑覆盖索引。

2

我不会改变表中的主键以包含辅助列......最好只在状态字段中添加一个新的非集合索引。

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