索引是否与" IN"子句一起使用

时间:2020-03-05 18:44:11  来源:igfitidea点击:

如果我有类似的查询:

Select EmployeeId 
From Employee 
Where EmployeeTypeId IN (1,2,3)

并且我在EmployeeTypeId字段上有一个索引,SQL Server仍然使用该索引吗?

解决方案

回答

通常,除非IN子句覆盖过多的表,否则它将进行表扫描。在特定情况下查找的最佳方法是在查询分析器中运行它,并签出执行计划。

回答

是啊,没错。如果雇员表具有10,000条记录,并且只有5条记录在(1,2,3)中具有employeetypeID,则它很可能使用索引来获取记录。但是,如果发现9,000条记录的(1,2,3)中有employeeIDType,则很可能只是执行表扫描以获取相应的EmployeeID,因为仅遍历整个表比转到表要快。索引树的每个分支,并分别查看记录。

SQL Server做了大量工作来尝试优化查询的运行方式。但是,有时无法得到正确的答案。如果我们知道SQL Server没有使用索引,可以通过查询查询分析器中的执行计划,告诉查询引擎使用特定的索引,并对查询进行以下更改。

Select EmployeeId From Employee WITH (Index(Index_EmployeeTypeId )) Where EmployeeTypeId IN (1,2,3)

假设我们在EmployeeTypeId字段上拥有的索引名为Index_EmployeeTypeId。

回答

So there's the potential for an "IN" clause to run a table scan, but the optimizer will 
  try and work out the best way to deal with it?

是否使用索引在查询类型上并没有多少不同,表中数据的类型和分布,表统计信息的最新程度以及列的实际数据类型都没有太大不同。

其他张贴者是正确的,如果出现以下情况,将在表扫描上使用索引:

  • 查询访问的索引行数不会超过特定百分比(例如〜10%,但应在DBMS之间有所不同)。
  • 或者,如果有很多行,但该列中的唯一值相对较少,那么执行表扫描也可能会更快。

另一个可能不太明显的变量是确保要比较的值的数据类型相同。在PostgreSQL中,我不认为如果在浮点数上进行过滤,但是列是由整数组成的,则不会使用索引。还有一些不支持索引使用的运算符(同样,在PostgreSQL中,ILIKE运算符就是这样)。

如上所述,如有疑问,请始终检查查询分析器,并且DBMS文档是朋友。

回答

除非技术以我最近无法想象的方式得到改进,否则显示的" IN"查询将产生一个有效地对三个结果集进行"或者"运算的结果,每个结果集用于" IN"列表中的每个值。 IN子句成为每个列表的相等条件,并将在适当时使用索引。如果使用唯一的ID和足够大的表,那么我希望优化器使用索引。

但是,如果列表中的项不是唯一的,并且在示例中我猜" TypeId"是外键,那么我对分发更感兴趣。我想知道优化程序是否将检查列表中每个值的统计信息?假设它检查了第一个值,发现它在(足够大的表可以使用的)20%的行中。它可能会进行表扫描。但是,即使其他查询是唯一的,也将对其他两个查询使用相同的查询计划吗?

可能会出现类似Employee表这样的问题,它可能足够小,可以保留在内存中,并且我们无论如何也不会注意到索引检索与索引检索之间的区别。

最后,在我讲课时,请注意IN子句中的查询:这通常是使某项工作正常的一种快速方法,并且(至少对我而言)可以是表达要求的一种好方法,但是几乎总是将其重述为一个联接。优化器可能足够聪明,可以发现这一点,但事实并非如此。如果我们当前不根据生产数据量进行性能检查,那么在这些基于成本的优化的日子里,我们将无法确定查询计划,除非我们有完整的负载和具有代表性的统计数据。如果我们做不到,那么请为生产中的意外做好准备...

回答

@迈克:感谢详细分析。我们肯定会在其中提出一些有趣的观点。我发布的示例有些琐碎,但问题的基础来自使用NHibernate。

使用NHibernate,我们可以编写如下子句:

int[] employeeIds = new int[]{1, 5, 23463, 32523};
NHibernateSession.CreateCriteria(typeof(Employee))
.Add(Restrictions.InG("EmployeeId",employeeIds))

NHibernate然后生成一个查询,看起来像

select * from employee where employeeid in (1, 5, 23463, 32523)

因此,正如我们和其他人指出的那样,似乎有时会使用索引或者进行表扫描,但是直到运行时我们才能真正确定。