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查找:到堆表里找其他字段的值

 

您可能感兴趣的与本文相关的镜像

Langchain-Chatchat

Langchain-Chatchat

AI应用
Langchain

Langchain-Chatchat 是一个基于 ChatGLM 等大语言模型和 Langchain 应用框架实现的开源项目,旨在构建一个可以离线部署的本地知识库问答系统。它通过检索增强生成 (RAG) 的方法,让用户能够以自然语言与本地文件、数据库或搜索引擎进行交互,并支持多种大模型和向量数据库的集成,以及提供 WebUI 和 API 服务

<think>嗯,用户问的是PostgreSQL中IN和OR查询是否会使用索引。这个问题很实际,确实需要仔细分析数据库的索引使用机制。 先回忆下索引合并的基本原理。根据引用[2],PostgreSQL确实支持组合多个索引扫描,通过位图操作合并结果。特别是OR条件查询时,数据库会把查询拆分成多个子句,每个子句独立索引扫描,最后用BITMAP OR合并。 对于IN查询,本质上可以看作多个OR条件的简写形式。比如WHERE id IN (1,2,3)其实等价于WHERE id=1 OR id=2 OR id=3。所以理论上应该适用同样的优化机制。引用[4]提到空间包含OR查询时就会索引合并的路径。 不过要注意几个关键点:一是索引类型必须支持等值查询,比如B-tree索引就特别适合;二是参数数量不能太多,否则优化器可能认为全表扫描更快;三是NULL值处理要小心,引用[3]特别指出WHERE column_name = NULL会导致索引失效,应该用IS NULL。 用户可能还关心实际性能。引用[4]提到一个潜在问题:位图扫描会返回BLOCKID而非精确的CTID,导致需要recheck操作。当IN列表很大时,这个开销可能比较明显。这时候可能需要调整work_mem参数或考虑其他优化手段。 突然想到个例外场景:如果字段选择性很差,比如性别字段只有两个取值,用IN查询可能反而不索引。因为数据库会计算成本,全表扫描可能更快。这解释了为什么有时候EXPLAIN看到Seq Scan。 最后考虑如何组织回答。应该先明确结论:IN/OR查询可以利用索引,再解释实现原理,然后补充性能注意事项。这样既解答了核心问题,又提供了优化方向。</think>在 PostgreSQL 中,**IN 和 OR 查询条件通常可以利用索引**,但具体能否使用以及使用效率如何,取决于查询的具体写法、索引类型、数据分布和优化器决策。以下是详细分析: --- ### **1. IN 查询的索引使用** - **支持索引扫描**: 当查询条件是 `WHERE column IN (value1, value2, ...)` 时: - 如果 `column` 上有 **B-tree 索引**,PostgreSQL 会将其转换为等价的 `OR` 条件(如 `WHERE column = value1 OR column = value2 ...`),然后通过 **Bitmap Index Scan** 合并多个索引扫描结果[^2][^4]。 - 示例: ```sql -- 假设在 id 字段有 B-tree 索引 EXPLAIN SELECT * FROM table WHERE id IN (1, 3, 5); ``` 执行计划可能显示: ``` Bitmap Heap Scan on table -> BitmapOr -> Bitmap Index Scan using idx_id on table (id=1) -> Bitmap Index Scan using idx_id on table (id=3) -> Bitmap Index Scan using idx_id on table (id=5) ``` - **注意事项**: - **列表长度过大时可能失效**:如果 `IN` 列表过长(如超过数百项),优化器可能认为全表扫描比多次索引扫描更快,从而放弃使用索引[^2]。 - **NULL 值问题**:`IN (NULL)` 无法匹配任何值(需改用 `IS NULL`),且 `WHERE column = NULL` 会导致索引失效[^3]。 --- ### **2. OR 查询的索引使用** - **多字段 OR 条件**: 对于 `WHERE cond1 OR cond2`: - 若 `cond1` 和 `cond2` 分别有独立索引,PostgreSQL 会通过 **BitmapOr** 合并两个索引的结果[^2][^4]。 - 示例: ```sql -- 假设在 col1 和 col2 上均有索引 EXPLAIN SELECT * FROM table WHERE col1 = 10 OR col2 = 20; ``` 执行计划可能包含: ``` BitmapOr -> Bitmap Index Scan on idx_col1 -> Bitmap Index Scan on idx_col2 ``` - **单字段多 OR 条件**: 如 `WHERE col = 1 OR col = 2 OR col = 3`,其行为与 `IN` 查询一致(见上文)。 --- ### **3. 索引失效的场景** 尽管优化器会尽量使用索引,但以下情况可能导致索引失效: 1. **非等值条件**: 如 `WHERE col > 10 OR col < 5`,若索引未覆盖范围查询,可能无法高效合并。 2. **跨列 OR 且无复合索引**: 例如 `WHERE col1 = 1 OR col2 = 2`,若没有同时覆盖 `col1` 和 `col2` 的复合索引,优化器可能选择全表扫描。 3. **低选择性字段**: 若字段重复值过多(如性别),优化器可能认为全表扫描更快。 4. **数据类型不匹配**: 如 `WHERE text_col = 123`(数值与文本比较),索引会失效。 --- ### **4. 性能优化建议** - **控制 `IN` 列表长度**:避免超过数百项,可改用临时表或 `JOIN`。 - **使用复合索引**:对频繁组合查询的字段(如 `(col1, col2)`)创建复合索引。 - **调整 `work_mem`**:增大内存参数可提升 Bitmap 操作效率[^2]。 - **监控执行计划**:通过 `EXPLAIN ANALYZE` 验证索引是否生效。 --- ### **结论** ✅ **是**:PostgreSQL 能对 `IN` 和 `OR` 查询使用索引,通过 **Bitmap Index Scan + BitmapOr** 合并结果[^2][^4]。 ⚠️ **但需注意**:索引是否实际生效取决于查询复杂度、索引设计及数据分布。建议通过 `EXPLAIN` 验证具体查询的执行计划。 --- ### 相关问题 1. **如何判断 PostgreSQL 查询是否使用了索引?** 2. **哪些类型的索引(如 B-tree、GIN、GiST)支持 `IN` 和 `OR` 查询?** 3. **当 `IN` 列表过长时,如何优化查询性能?** 4. **PostgreSQL 的位图扫描(Bitmap Scan)机制如何工作?** 5. **为什么 `WHERE column = NULL` 会导致索引失效?应如何正确查询 NULL 值?**[^3] [^1]: 索引合并原理 [^2]: PostgreSQL 多索引组合机制 [^3]: NULL 值查询与索引失效 [^4]: OR 查询的索引合并与优化
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值