Select * 一定不走索引是否正确?

本文详细解释了SQL中聚集索引和非聚集索引的工作原理,包括如何根据查询条件选择合适的索引进行数据检索,以及不同场景下索引的选择策略。

走索引指的是:SQL语句的执行计划用到了1、聚集索引查找  2、索引查找  ,并且查询语句中需要有where子句

根据where子句的过滤条件,去聚集索引或非聚集索引那里查找记录

一张表只有一列的情况:

聚集索引

 
 USE [tempdb]
 GO
 CREATE TABLE t1 ( id INT )
 GO
 CREATE CLUSTERED INDEX CIX_T1 ON [dbo].[t1](ID ASC)
 GO


 DECLARE @I INT
 SET @I = 1
 WHILE @I < 1000 
     BEGIN
         INSERT  INTO [dbo].[t1] ( [id] )
                 SELECT  @I
         SET @I = @I + 1
     END

 SELECT * FROM [dbo].[t1] WHERE [id]=20
View Code

 

非聚集索引

 
 USE [tempdb]
 GO
 
 CREATE TABLE t2 ( id INT )
 GO
 CREATE NONCLUSTERED INDEX IX_T2 ON [dbo].[t2](ID ASC)
 GO


 DECLARE @I INT
 SET @I = 1
 WHILE @I < 1000 
     BEGIN
         INSERT  INTO [dbo].[t2] ( [id] )
                 SELECT  @I
         SET @I = @I + 1
     END

 SELECT * FROM [dbo].[t2] WHERE [id]=20
View Code

只有一列,肯定会走索引的


一张表有多列的情况

分三种情况:

1、只有聚集索引

2、只有非聚集索引

3、有聚集索引和非聚集索引


只有聚集索引

 --只有聚集索引
 USE [tempdb]
 GO
 CREATE TABLE Department  
 (
   DepartmentID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
   Name NVARCHAR(200) NOT NULL ,
   GroupName NVARCHAR(200) NOT NULL ,
   Company NVARCHAR(300) ,
   ModifiedDate DATETIME NOT NULL DEFAULT ( GETDATE() ) 

 )

 
 DECLARE @i INT
 SET @i=1
 WHILE @i < 100000 
     BEGIN
         INSERT  INTO Department ( name, [Company], groupname )
         VALUES  ( '销售部'+CAST(@i AS VARCHAR(200)), '中国你好有限公司XX分公司', '销售组' )
         SET @i = @i + 1
     END


 SELECT * FROM [dbo].[Department] WHERE [DepartmentID]=2
View Code

小结:

只有聚集索引的表:如果where后面不包括创建聚集索引的时候的第一个字段,就会使用聚集索引扫描

下面SQL语句会使用聚集索引查找,因为包括了创建聚集索引的时候的第一个字段

 SELECT * FROM [dbo].[Department] WHERE [Company]='销售部12' AND [DepartmentID]=12

只有非聚集索引

 
 --只有非聚集索引
 USE [tempdb]
 GO

 CREATE TABLE Department  
 (
   DepartmentID INT IDENTITY(1, 1) NOT NULL ,
   Name NVARCHAR(200) NOT NULL ,
   GroupName NVARCHAR(200) NOT NULL ,
   Company NVARCHAR(300) ,
   ModifiedDate DATETIME NOT NULL DEFAULT ( GETDATE() ) 
 
 )
 
 CREATE NONCLUSTERED INDEX IX_Department ON Department(DepartmentID ASC)

 DECLARE @i INT
 SET @i=1
 WHILE @i < 100000 
     BEGIN
         INSERT  INTO Department ( name, [Company], groupname )
         VALUES  ( '销售部'+CAST(@i AS VARCHAR(200)), '中国你好有限公司XX分公司', '销售组' )
         SET @i = @i + 1
     END
 
SELECT * FROM [dbo].[Department] WHERE [Company]='销售部12' AND [DepartmentID]=12
View Code

 

小结:

只有非聚集索引的表:如果where后面不包括创建非聚集索引的时候的第一个字段,就会使用表扫描或者索引扫描

下面SQL语句会使用非聚集索引查找,因为包括了创建非聚集索引的时候的第一个字段

 SELECT * FROM [dbo].[Department] WHERE [Company]='销售部12' AND [DepartmentID]=12

 


有聚集索引也有非聚集索引

复制代码
 --有聚集索引和非聚集索引
 USE [tempdb]
 GO
 
 CREATE TABLE Department  
(
  DepartmentID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
  Name NVARCHAR(200) NOT NULL ,
  GroupName NVARCHAR(200) NOT NULL ,
  Company NVARCHAR(300) ,
  ModifiedDate DATETIME NOT NULL DEFAULT ( GETDATE() ) 
 
)
 
CREATE NONCLUSTERED INDEX IX_Department ON Department(Company ASC)
 

 DECLARE @i INT
 SET @i=1
 WHILE @i < 100000 
     BEGIN
         INSERT  INTO Department ( name, [Company], groupname )
         VALUES  ( '销售部'+CAST(@i AS VARCHAR(200)), '中国你好有限公司XX分公司', '销售组' )
         SET @i = @i + 1
     END
View Code

小结:

有聚集索引和非聚集索引的表:如果where后面包括创建聚集索引的时候的第一个字段,就会使用聚集索引查找

如果where后面包括创建非聚集索引的时候的第一个字段但不包括创建聚集索引的时候的第一个字段,就会使用索引查找

如果where后面不包括创建非聚集索引的时候的第一个字段和不包括创建聚集索引的时候的第一个字段,就会使用聚集索引扫描

1 SELECT * FROM [dbo].[Department] WHERE [GroupName]='销售组'


总结

其实走不走索引,关键取决于where后面包括还是不包括

创建聚集索引的时候的第一个字段

创建非聚集索引的时候的第一个字段

跟select *没有关系的,select * 最大的影响就是额外的IO开销

像“键查找” ,“RID查找”这些运算符就是额外的开销

键查找:到聚集索引里找其他字段的值

RID查找:到堆表里找其他字段的值

 

<think>首先,用户的问题是关于MySQL查询的。用户提到在查询中,createtime字段有索引,但使用范围查询如createtime >= ? 和 createtime < ? 时,出现了索引下推(Index Condition Pushdown, ICP)。用户怀疑createtime < ? 是否导致索引失效,并问及非索引字段作为查询条件是否影响索引使用索引下推(ICP)是MySQL的一个优化特性,它允许在索引扫描过程中应用WHERE条件的部分,从而减少需要访问表的数据行数。这通常发生在复合索引或范围查询中。 用户的问题要点: 1. createtime字段有索引。 2. 使用范围查询:createtime >= ? and createtime < ? 3. 出现索引下推。 4. 怀疑createtime < ? 是否导致索引失效。 5. 非索引字段作为查询条件是否影响索引使用。 我需要参考提供的引用内容: - 引用[1]:关于创建索引- 引用[2]:如果MySQL评估使用索引比全表扫描更慢,则使用索引。特别是is null和is not null的情况。 - 引用[3]:SQL提示,如use index, ignore index, force index。 - 引用[4]:字符串加单引号导致索引失效。 这些引用中,引用[2]和[4]与索引失效相关,但用户的问题更具体于范围查询和索引下推。 关键点: - 索引下推(ICP)是MySQL 5.6引入的特性,它允许在存储引擎层过滤数据,而需要将所有行发送到服务器层。 - 在范围查询中,索引通常可以被使用,但范围条件如createtime < ? 可能会导致索引失效;它可能只使用索引的一部分。 - 索引下推发生当有额外的WHERE条件可以在索引中被评估。 用户提到“createtime <? 导致索引失效”,但根据标准MySQL行为,范围查询本身会导致索引失效;它可能会使索引只用于范围部分,而后续条件可能需要回表检查。 索引下推的典型场景:当查询有范围条件和其他条件时,如果其他条件可以基于索引列评估,ICP可以推这些条件到索引扫描中。 例如,如果有一个索引在createtime,查询是: SELECT * FROM table WHERE createtime >= ? AND createtime < ? AND some_other_column = ? 如果some_other_column没有索引,但ICP开启,MySQL可能会在索引扫描时应用createtime条件,并尝试应用some_other_column条件如果可能,但通常some_other_column如果没有索引,需要回表。 ICP主要用于当索引包含多个列时,在复合索引中推非范围条件。 用户说“createtime字段有索引”,我假设是单列索引在createtime上。 查询是:createtime >= ? AND createtime < ? AND other_condition other_condition可能涉及非索引字段。 现在,索引下推出现的原因:如果other_condition可以部分在索引中被评估,但createtime是单列索引,所以索引只包含createtime,因此other_condition能在索引中被评估,除非是覆盖索引或其他。 索引下推要求条件基于索引列。对于单列索引在createtime上,索引只包含createtime值,所以任何其他条件如other_condition能直接在索引中被评估;它需要访问表行。 因此,在单列索引上,对于范围查询,索引下推可能会发生,因为没有其他索引列来推条件。 用户说“出现索引下推”,所以可能索引是复合索引?用户没有指定索引类型,但说“createtime字段有索引”,可能暗示单列索引。 用户提到“其他查询条件字段没有索引”,所以很可能查询中有额外的条件使用索引字段。 例如:SELECT * FROM table WHERE createtime >= ? AND createtime < ? AND non_indexed_column = ? 在这种情况,createtime索引可以被用于范围扫描,但non_indexed_column条件需要在回表后检查。 索引下推(ICP)在MySQL中,当索引是复合索引时,可以推索引列的条件。但如果是单列索引,ICP可能适用。 或许用户误解了“索引下推”。或者索引是复合索引,包含createtime和其他列。 用户没有指定索引结构,所以我需要基于一般情况回答。 回答结构: 1. 解释索引在范围查询中的使用。 2. 解释索引下推(ICP)是什么。 3. 分析createtime < ? 是否导致索引失效。 4. 讨论非索引字段作为查询条件的影响。 5. 基于引用提供建议。 从引用[2]:MySQL可能使用索引如果评估全表扫描更快。对于范围查询,索引可能被部分使用。 引用[4]:类型转换导致索引失效,但这里没有类型转换问题。 现在,针对用户的问题: - createtime < ? 会导致索引失效;范围查询可以使用索引- 索引下推出现可能是因为查询中有其他条件可以被推,但需要具体分析。 -索引字段作为查询条件会使索引失效,但可能降低效率,因为需要回表检查这些条件。 索引下推的条件:MySQL文档说,ICP用于当索引可以用于评估部分WHERE条件时。对于InnoDB表,ICP可以减少回表次数。 在范围查询后,如果有其他条件基于索引列,ICP可以推这些条件。 但在单列索引,只有createtime,所以没有其他索引列来推条件。因此,如果索引是单列,ICP可能发生。 用户说“出现索引下推”,所以可能索引是复合索引?或者用户看到EXPLAIN输出显示"Using index condition"。 在EXPLAIN输出中,"Using index condition"表示ICP被使用。 所以,对于查询WHERE createtime >= ? AND createtime < ? AND other_col = ?,如果索引是复合索引(createtime, other_col),那么ICP可以推other_col条件。 但用户说“其他查询条件字段没有索引”,所以other_col可能没有索引,因此如果索引只包含createtime,ICP能用于other_col。 或许createtime索引是单列,但ICP被用于其他部分? ,ICP要求条件基于索引列。 所以,可能用户有复合索引,或者误解。 在回答中,我应该澄清。 步骤: 1. 确认范围查询可以使用索引。 2. 解释索引下推。 3. 讨论createtime < ? 是否失效索引:通常会。 4. 非索引字段的影响:需要回表,可能降低性能。 5. 建议使用复合索引或优化查询。 生成相关问题。 回答结构: - 介绍索引在范围查询中的行为。 - 解释索引下推。 - 分析具体问题。 - 提供优化建议。 - 生成相关问题。 使用中文回答。</think>### MySQL索引在范围查询中的行为与索引下推分析 在MySQL查询中,当对已添加索引的字段(如`createtime`)使用范围条件(例如`createtime >= ? AND createtime < ?`)时,索引的行为和索引下推(Index Condition Pushdown, ICP)的出现涉及多个因素。下面我将逐步解释索引使用机制、索引下推的原因、`createtime < ?`是否导致索引失效,以及非索引字段作为查询条件的影响。回答基于MySQL的InnoDB存储引擎(默认引擎)和优化器行为,并结合您提供的引用内容进行分析。 #### 1. **索引在范围查询中的基本行为** - **索引通常会被范围查询本身“失效”**:当`createtime`字段有索引(假设是单列索引或复合索引的一部分)时,范围查询条件如`createtime >= ? AND createtime < ?` 可以使用索引进行范围扫描(Range Scan)。MySQL会利用索引快速定位到满足`createtime`范围的行,而需要全表扫描。这能显著提高查询效率,尤其是当范围较小时。 - **`createtime < ?` 会单独导致索引失效**:在范围查询中,`createtime < ?` 是有效的索引条件。MySQL优化器会将整个范围条件(包括上限和下限)用于索引扫描。索引失效通常发生在其他场景,如: - 数据类型匹配(例如,字符串比较时未加单引号,导致隐式类型转换,如引用[4]所述)。 - MySQL评估使用索引比全表扫描更慢(例如,当表中满足条件的行数很多,或索引选择性低时,如引用[2]所述)。 - 在您的查询中,如果`createtime`的值分布均匀,且范围条件覆盖的行数较少,索引通常会有效使用。 在`EXPLAIN`输出中,如果看到`type: range`,表示索引正被用于范围扫描。索引未失效时,会出现`type: ALL`(全表扫描)。 #### 2. **索引下推(ICP)的原因和机制** - **什么是索引下推(ICP)?** 索引下推是MySQL 5.6及以上版本的优化特性(默认开启)。它允许存储引擎(如InnoDB)在索引扫描过程中,提前应用WHERE条件的部分内容(即“推”到存储引擎层),从而减少需要回表(访问实际数据行)的次数。这能降低I/O开销和CPU负载。在`EXPLAIN`输出中,ICP表现为`Extra: Using index condition`。 - **为什么在您的查询中出现索引下推?** 在您的场景中,ICP的出现可能由于以下原因: - **查询中还有其他条件**:如果查询包含除`createtime`范围外的其他WHERE条件(例如`AND non_indexed_column = ?`),且这些条件涉及非索引字段,MySQL会尝试在索引扫描阶段“下推”部分过滤逻辑。 - **ICP的工作逻辑**- 对于单列索引(仅`createtime`):ICP无法直接应用于非索引字段(如`non_indexed_column`),因为索引只包含`createtime`值。但ICP仍可用于优化索引本身的扫描(例如,更精确地过滤范围边界)。 - 对于复合索引(例如`(createtime, other_column)`):如果索引包含多个列,ICP可以将其他索引列的条件(如`other_column = ?`)下推到存储引擎层评估,即使有范围条件。但在您的问题中,您提到“其他查询条件字段没有索引”,所以复合索引可能存在,ICP的作用可能有限。 - **ICP的触发条件**:即使是非索引字段条件,MySQL也可能在索引扫描后、回表前,尝试在存储引擎层应用这些条件(如果优化器认为可行)。这能减少回表行数。例如: - 查询:`SELECT * FROM table WHERE createtime >= ? AND createtime < ? AND status = 0`(假设`status`无索引)。 - ICP可能发生:存储引擎先用索引找到满足`createtime`范围的行ID,然后在这些行ID上应用`status = 0`条件(如果`status`值在索引元数据中可访问部分信息),避免所有行都回表。 - **为什么ICP出现?** 在范围查询中,索引扫描返回的行数可能较多。ICP帮助减少必要的回表操作,尤其当非索引字段条件能过滤掉大量行时。MySQL优化器基于成本估算自动决定是否使用ICP,如果评估ICP能提升性能,它就会启用(如引用[3]中提到的优化器评估机制)。 总之,ICP的出现是MySQL的主动优化,表明索引未被完全失效,而是被更高效地利用来处理复杂查询条件。 #### 3. **`createtime < ?`是否导致索引失效?** - **会直接导致失效**:`createtime < ?`作为范围上限,与`createtime >= ?`一起构成一个有效的范围查询,索引通常能被使用索引失效的常见原因包括: - **数据分布问题**:如果`createtime < ?`覆盖了表中大部分行(例如,超过30%),MySQL可能评估全表扫描更快,从而忽略索引(如引用[2]所述,当索引扫描成本更高时)。 - **其他因素**:如查询涉及`OR`条件、函数操作(如`DATE(createtime)`),或类型匹配(引用[4]的字符串示例)。但单纯的`<`操作符会引起失效。 - **验证方法**使用`EXPLAIN`分析查询。如果`possible_keys`和`key`显示`createtime`索引被选中,且`type`为`range`,则索引有效。如果索引失效,`key`会为`NULL`。 #### 4. **索引字段作为查询条件的影响** - **影响索引使用,但可能降低性能**:非索引字段(如`other_column`)在WHERE条件中会使`createtime`索引失效,但会引入额外开销: - **回表操作**索引扫描只能处理`createtime`条件,非索引字段条件必须在回表后检查(访问实际数据行)。这增加I/O和CPU消耗。 - **性能瓶颈**:如果非索引字段条件过滤性高(能排除多数行),ICP可能部分优化;但如果过滤性低,回表成本高,查询可能变慢。 - **优化建议**- **添加复合索引**:如果查询频繁使用`createtime`和另一个字段(如`status`),创建复合索引`(createtime, status)`。这样,ICP能直接下推`status`条件,避免回表(如引用[3]的覆盖索引概念)。 - **使用SQL提示**:如果MySQL优化器未选择索引,可尝试`FORCE INDEX`或`USE INDEX`提示(如引用[3])。例如: ```sql EXPLAIN SELECT * FROM table FORCE INDEX(idx_createtime) WHERE createtime >= ? AND createtime < ? AND status = 0; ``` - **避免全表扫描诱因**:确保查询条件无类型转换(如引用[4]),并定期分析表数据分布(使用`ANALYZE TABLE`),帮助优化器正确评估索引成本。 #### 5. **优化建议总结** - **索引策略**:优先使用复合索引覆盖常用查询条件。例如,对`createtime`和常查询的非索引字段创建索引- **监控和调优**使用`EXPLAIN`检查查询计划,确认`Using index condition`是否出现。如果ICP未启用或索引未被使用,检查是否因数据分布导致(如引用[2]的`is null`案例)。 - **参数调整**:在MySQL配置中,确认`optimizer_switch`的`index_condition_pushdown=on`(默认开启)。如果怀疑优化器错误,可暂时关闭ICP测试:`SET optimizer_switch='index_condition_pushdown=off';`。 - **一般原则**索引在范围查询中有效,但性能取决于范围大小和数据分布。非索引字段会增加回表开销,建议通过索引或查询重写优化。 ### 相关问题 1. 如何通过`EXPLAIN`分析MySQL查询的执行计划,并识别索引使用情况? 2. 在复合索引中,范围查询(如`createtime < ?`)对索引最左前缀原则有何影响? 3. 索引下推(ICP)在哪些场景下效果最显著?如何测量其对查询性能的提升? 4. 当表中存在大量数据时,如何避免范围查询导致的全表扫描或索引失效? 5. MySQL中,除了索引下推,还有哪些查询优化技术(如覆盖索引)可以减少回表操作? 如果您提供具体的表结构、索引定义和查询示例,我可以进一步分析优化方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值