如果我有这样的查询:
Select EmployeeId
From Employee
Where EmployeeTypeId IN (1,2,3)
我在该EmployeeTypeId
字段上有一个索引,SQL Server 是否仍然使用该索引?
是的,没错。如果您的Employee
表有 10,000 条记录,而EmployeeTypeId
(1,2,3) 中只有 5 条记录,那么它很可能会使用索引来获取记录。但是,如果它发现 9,000 条记录具有EmployeeTypeId
in (1,2,3),那么它很可能只是进行表扫描以获取相应EmployeeId
的 s,因为它只遍历整个表比遍历每个表要快索引树的分支并单独查看记录。
SQL Server 做了很多工作来尝试优化查询的运行方式。然而,有时它并没有得到正确的答案。如果您知道 SQL Server 没有使用索引,通过查看查询分析器中的执行计划,您可以告诉查询引擎使用特定索引,并对查询进行以下更改。
SELECT EmployeeId FROM Employee WITH (Index(Index_EmployeeTypeId )) WHERE EmployeeTypeId IN (1,2,3)
假设您在该EmployeeTypeId
字段上拥有的索引名为Index_EmployeeTypeId
.
通常它会,除非 IN 子句覆盖了太多的表,然后它会进行表扫描。找出具体情况的最佳方法是在查询分析器中运行它,并检查执行计划。
所以“IN”子句有可能运行表扫描,但优化器会尝试找出处理它的最佳方法?
是否使用索引与查询类型的差异不大,而与表中数据的类型和分布、表统计信息的最新程度以及列的实际数据类型不同.
其他海报是正确的,在以下情况下将在表扫描中使用索引:
另一个可能不那么明显的变量是确保被比较的值的数据类型是相同的。在 PostgreSQL 中,如果您在浮点数上进行过滤,但您的列由整数组成,我认为不会使用索引。还有一些运算符不支持使用索引(同样,在 PostgreSQL 中,ILIKE 运算符是这样的)。
如前所述,如有疑问,请务必检查查询分析器,并且您的 DBMS 文档是您的朋友。
除非技术以我无法想象的方式改进,否则显示的“IN”查询将产生一个有效地对三个结果集进行 OR-ing 的结果,一个对应于“IN”列表中的每个值。IN 子句成为每个列表的相等条件,并在适当时使用索引。在唯一 ID 和足够大的表的情况下,我希望优化器使用索引。
但是,如果列表中的项目不是唯一的,并且我猜在示例中“TypeId”是外键,那么我对分布更感兴趣。我想知道优化器是否会检查列表中每个值的统计信息?假设它检查第一个值并发现它在 20% 的行中(在一个足够大的表中)。它可能会进行表扫描。但是,其他两个查询计划是否会使用相同的查询计划,即使它们是唯一的?
这可能没有实际意义——像 Employee 表之类的东西可能足够小,以至于它会一直缓存在内存中,而且您可能不会注意到它与索引检索之间的区别。
最后,当我在讲道时,请注意 IN 子句中的查询:它通常是一种让某些东西工作的快速方法,并且(至少对我而言)可以是表达需求的好方法,但它几乎总是更好地重申为加入。您的优化器可能足够聪明地发现这一点,但又可能不会。如果您目前没有针对生产数据量进行性能检查,那么请这样做 - 在这些基于成本的优化的日子里,您无法确定查询计划,直到您拥有完整的负载和代表性的统计数据。如果你做不到,那就为生产中的惊喜做好准备......
@Mike:感谢您的详细分析。你肯定有一些有趣的观点。我发布的示例有些微不足道,但问题的基础来自使用 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)
因此,正如您和其他人所指出的那样,看起来有时会使用索引或会发生表扫描,但直到运行时您才能真正确定这一点。
Select EmployeeId From Employee USE(INDEX(EmployeeTypeId))
此查询将使用您创建的索引进行搜索。这个对我有用。请试一试。。