如何优化带or条件的sql

本文探讨了在Oracle数据库中如何通过创建不同的索引来优化复杂查询的性能,并对比了使用UNION和UNION ALL关键字时的不同执行效果。

以下为转帖内容:

================================================================================================

今天在论坛上看到了一个帖子,问题如下:

select *   from cc
where ((a1 ='ffff' and z1='mmmm') or (b1='sss' and z2='nnnn'))
and c1 ='ggggg'
其中表有30万行数据,返回的数据10行左右,怎样创建index访问最快。

     按照别人的说法测试了一下,步骤如下:

create table CC
(
A1 VARCHAR2(5),
Z1 VARCHAR2(5),
B1 VARCHAR2(5),
Z2 VARCHAR2(5),
C1 VARCHAR2(5)
)

insert into cc values('dffd','dfsd','fdf','fdsfs','sfds');--重复插入2097152条,对查询时间可能有影响


SQL> select count(*) from cc;

COUNT(*)
----------
   2097160

SQL> set timing on
SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where ((a1='ffff'and z1='mmmm') or (b1='sss' and z2='nnnn'))
3* and c1='ggggg'--无索引情况下or查询
SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg

已选择8行。

已用时间: 00: 00: 00.21
SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where (a1='ffff'and z1='mmmm')
3 and c1='ggggg'
4 union
5 select * from cc
6 where (b1='sss' and z2='nnnn')
7* and c1='ggggg'--无索引情况下union查询,注意与union all查询结果的区别
SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg

已用时间: 00: 00: 00.33
SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where (a1='ffff'and z1='mmmm')
3 and c1='ggggg'
4 union all
5 select * from cc
6 where (b1='sss' and z2='nnnn')
7* and c1='ggggg'--无索引情况下union all查询
SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg

已选择8行。

已用时间: 00: 00: 00.35
SQL> create index cc_idx on cc(c1);

索引已创建。

已用时间: 00: 00: 11.14
SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where ((a1='ffff'and z1='mmmm') or (b1='sss' and z2='nnnn'))
3* and c1='ggggg'--有索引or查询,注意,虽然没有列出执行计划,索引肯定用到了。

SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg

已选择8行。

已用时间: 00: 00: 00.01
SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where (a1='ffff'and z1='mmmm')
3 and c1='ggggg'
4 union
5 select * from cc
6 where (b1='sss' and z2='nnnn')
7* and c1='ggggg'--有索引union查询,注意与union all查询结果的区别
SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg

已用时间: 00: 00: 00.00
SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where (a1='ffff'and z1='mmmm')
3 and c1='ggggg'
4 union all
5 select * from cc
6 where (b1='sss' and z2='nnnn')
7* and c1='ggggg'--有索引union all查询
SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg

已选择8行。

已用时间: 00: 00: 00.01

SQL>create index CC_IDX2 on CC (A1, Z1);

SQL>create index CC_IDX3 on CC (B1, Z2);

SQL>set autot on

SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where ((a1='ffff'and z1='mmmm') or (b1='sss' and z2='nnnn'))
3* and c1='ggggg'--3索引情况下or查询

SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg

已选择8行。

已用时间: 00: 00: 00.60--时间明显比单索引扫描时间长

执行计划
----------------------------------------------------------
Plan hash value: 1540710700

---------------------------------------------------------------------------
| Id | Operation          | Name | Rows | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     2 |    40 |     4   (0)| 00:00:01 |
|   1 | CONCATENATION     |      |       |       |            |          |
|* 2 |   TABLE ACCESS FULL| CC   |     1 |    20 |     2   (0)| 00:00:01 |--注意:从执行计划上可以看到,
|* 3 |   TABLE ACCESS FULL| CC   |     1 |    20 |     2   (0)| 00:00:01 |--索引失效,全表扫描
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("B1"='sss' AND "Z2"='nnnn' AND "C1"='ggggg')
   3 - filter("C1"='ggggg' AND "A1"='ffff' AND "Z1"='mmmm' AND
              (LNNVL("B1"='sss') OR LNNVL("Z2"='nnnn')))

Note
-----
   - dynamic sampling used for this statement


统计信息
----------------------------------------------------------
          0 recursive calls
          0 db block gets
      17673 consistent gets
        405 physical reads
          0 redo size
        703 bytes sent via SQL*Net to client
        400 bytes received via SQL*Net from client
          2 SQL*Net roundtrips to/from client
          0 sorts (memory)
          0 sorts (disk)
          8 rows processed

SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where (a1='ffff'and z1='mmmm')
3 and c1='ggggg'
4 union
5 select * from cc
6 where (b1='sss' and z2='nnnn')
7* and c1='ggggg'--3索引情况下union查询,注意与union all查询结果的区别
SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg

已用时间: 00: 00: 00.10--时间明显比单索引扫描时间长

执行计划
----------------------------------------------------------
Plan hash value: 1185376162

--------------------------------------------------------------------------------

---------

| Id | Operation                     | Name    | Rows | Bytes | Cost (%CPU)| T

ime     |

--------------------------------------------------------------------------------

---------

|   0 | SELECT STATEMENT              |         |     4 |    80 |    10 (60)| 0

0:00:01 |

|   1 | SORT UNIQUE                  |         |     4 |    80 |    10 (60)| 0

0:00:01 |

|   2 |   UNION-ALL                   |         |       |       |            |
        |

|* 3 |    TABLE ACCESS BY INDEX ROWID| CC      |     2 |    40 |     4   (0)| 0

0:00:01 |

|* 4 |     INDEX RANGE SCAN          | CC_IDX2 |    34 |       |     3   (0)| 0

0:00:01 |

|* 5 |    TABLE ACCESS BY INDEX ROWID| CC      |     2 |    40 |     4   (0)| 0

0:00:01 |

|* 6 |     INDEX RANGE SCAN          | CC_IDX3 |    34 |       |     3   (0)| 0

0:00:01 |

--------------------------------------------------------------------------------

---------


Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("C1"='ggggg')
   4 - access("A1"='ffff' AND "Z1"='mmmm')
   5 - filter("C1"='ggggg')
   6 - access("B1"='sss' AND "Z2"='nnnn')

Note
-----
   - dynamic sampling used for this statement


统计信息
----------------------------------------------------------
          9 recursive calls
          0 db block gets
        174 consistent gets
          7 physical reads
          0 redo size
        637 bytes sent via SQL*Net to client
        400 bytes received via SQL*Net from client
          2 SQL*Net roundtrips to/from client
          1 sorts (memory)--注意,进行了排序
          0 sorts (disk)
          1 rows processed

SQL> edit
已写入 file afiedt.buf

1 select * from cc
2 where (a1='ffff'and z1='mmmm')
3 and c1='ggggg'
4 union all
5 select * from cc
6 where (b1='sss' and z2='nnnn')
7* and c1='ggggg'--3索引情况下union all查询
SQL> /

A1    Z1    B1    Z2    C1
----- ----- ----- ----- -----
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg
ffff mmmmm sss   nnnn ggggg

已选择8行。

已用时间: 00: 00: 00.06--时间明显比单索引扫描时间长

执行计划
----------------------------------------------------------
Plan hash value: 198920981

--------------------------------------------------------------------------------

--------

| Id | Operation                    | Name    | Rows | Bytes | Cost (%CPU)| Ti

me     |

--------------------------------------------------------------------------------

--------

|   0 | SELECT STATEMENT             |         |     4 |    80 |     8 (50)| 00

:00:01 |

|   1 | UNION-ALL                   |         |       |       |            |
       |

|* 2 |   TABLE ACCESS BY INDEX ROWID| CC      |     2 |    40 |     4   (0)| 00

:00:01 |

|* 3 |    INDEX RANGE SCAN          | CC_IDX2 |    34 |       |     3   (0)| 00

:00:01 |

|* 4 |   TABLE ACCESS BY INDEX ROWID| CC      |     2 |    40 |     4   (0)| 00

:00:01 |

|* 5 |    INDEX RANGE SCAN          | CC_IDX3 |    34 |       |     3   (0)| 00

:00:01 |

--------------------------------------------------------------------------------

--------


Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("C1"='ggggg')
   3 - access("A1"='ffff' AND "Z1"='mmmm')
   4 - filter("C1"='ggggg')
   5 - access("B1"='sss' AND "Z2"='nnnn')

Note
-----
   - dynamic sampling used for this statement


统计信息
----------------------------------------------------------
          7 recursive calls
          0 db block gets
        175 consistent gets
          0 physical reads
          0 redo size
        703 bytes sent via SQL*Net to client
        400 bytes received via SQL*Net from client
          2 SQL*Net roundtrips to/from client
          0 sorts (memory)
          0 sorts (disk)
          8 rows processed

SQL>

在数据库中,UNIONUNION ALL关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同。

UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。

实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION如:

select * from gc_dfys

union

select * from ls_jg_dfys

这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。

UNION ALL只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。

从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用UNION ALL。

这个从论坛上摘下来的:emp表比较大时,而且deptno = 10条件能查询出表中大部分的数据如(50%)。如该表共有4000万行数据,共放在有500000个数据块中,每个数据块为8k,则该表共有约4G,则这么多的数据不可能全放在内存中,绝大多数需要放在硬盘上。此时如果该查询通过索引查询,则是你梦魇的开始。db_file_multiblock_read_count参数的值200。如果采用全表扫描,则需要500000/db_file_multiblock_read_count=500000/200=2500次I/O。但是如果采用索引扫描,假设deptno列上的索引都已经cache到内存中,所以可以将访问索引的开销忽略不计。因为要读出4000万x 50% = 2000万数据,假设在读这2000万数据时,有99.9%的命中率,则还是需要20000次I/O,比上面的全表扫描需要的2500次多多了,所以在这种情况下,用索引扫描反而性能会差很多。在这样的情况下,用全表扫描的时间是固定的,但是用索引扫描的时间会随着选出数据的增多使查询时间相应的延长。

建立合适的索引,写适当的语句,才能达到最优。

<think>好的,我现在需要解决用户关于如何优化SQL查询中使用OR关键字的性能问题。首先,我需要回顾一下ORSQL查询中的工作原理以及可能导致的性能问题。根据引用[1]和[2],优化SQL通常涉及索引使用、查询重写和利用数据库的优化器特性。 首先,OR条件可能导致索引失效。比如,如果WHERE子句中有OR条件,每个条件可能需要不同的索引,这时候数据库可能无法有效使用索引,导致全表扫描。例如,假设有一个查询WHERE a=1 OR b=2,如果a和b各自有单独的索引,但缺乏联合索引,数据库可能选择全表扫描而不是索引合并,这会降低性能。 接下来,我需要考虑如何优化这种情况。根据引用[2],使用UNION或UNION ALL代替OR可能有效。例如,将查询拆分为两个部分,分别使用a=1和b=2,然后用UNION合并结果。这样每个部分可能利用各自的索引,提高查询效率。但需要注意UNION会去重,而UNION ALL不会,所以要根据实际情况选择。 另外,检查是否有覆盖索引的可能性。如果查询的列都在索引中,即使使用OR,数据库也可能通过索引扫描来避免访问表数据。比如,如果有一个联合索引(a, b),查询SELECT a, b FROM table WHERE a=1 OR b=2,可能这个索引会被使用,从而提升性能。 还要考虑OR条件是否可以用IN或CASE WHEN重写。例如,当OR条件在同一字段时,使用IN可能更高效。例如,WHERE id=1 OR id=2可以改写为WHERE id IN (1,2),这样可能更好地利用索引。 此外,数据库优化器的特性也很重要。有些数据库如MySQL在某些版本中对OR的处理可能不够优化,这时候手动拆分查询会更有效。同时,查看执行计划(EXPLAIN)是关键步骤,可以确认索引是否被正确使用以及查询的执行路径。 还要注意OR与其他条件组合时的情况,比如和LIKE或范围查询一起使用,可能导致更复杂的优化问题。可能需要重新设计查询结构或调整索引策略。 最后,总结优化方法:使用UNION替代OR、创建合适的索引、利用覆盖索引、重写查询条件、分析执行计划。同时需要根据具体数据库的实现和版本进行调整,测试不同方法的性能差异,选择最优方案。</think>### SQL查询中OR关键字的性能优化方法 使用OR关键字可能导致全表扫描或索引失效,以下是优化方法及最佳实践: #### 1. **使用UNION/UNION ALL替代OR** 将包含OR条件的查询拆分为个独立查询,通过UNION合并结果。例如: ```sql -- 原始查询 SELECT * FROM users WHERE age > 30 OR salary < 5000; -- 优化后 SELECT * FROM users WHERE age > 30 UNION ALL SELECT * FROM users WHERE salary < 5000; ``` - **优势**:每个子查询可能使用独立索引,避免全表扫描[^2]。 - **注意**:UNION会去重(消耗性能),若无重复数据则用UNION ALL。 --- #### 2. **创建复合索引或覆盖索引** 若OR条件涉及同一表的个字段,尝试创建复合索引: ```sql -- 假设字段a和b有独立索引,但OR条件无法同时生效 CREATE INDEX idx_a_b ON table(a, b); ``` - **覆盖索引**:若查询字段全部包含在索引中,可直接通过索引返回数据,无需回表。例如: ```sql SELECT a, b FROM table WHERE a=1 OR b=2; -- 若(a,b)有联合索引,可能触发索引扫描 ``` --- #### 3. **重写OR条件为IN或CASE WHEN** - **同一字段的OR条件**:用IN替代: ```sql -- 原始查询 SELECT * FROM products WHERE category='book' OR category='food'; -- 优化后 SELECT * FROM products WHERE category IN ('book', 'food'); ``` - **逻辑等价转换**:使用CASE WHEN或布尔逻辑简化条件: ```sql SELECT * FROM orders WHERE (status='paid' AND amount>100) OR (status='unpaid' AND amount>200); ``` --- #### 4. **利用数据库优化器特性** - **MySQL的索引合并优化**:某些版本支持`index_merge`,自动合并个索引扫描结果。通过`EXPLAIN`查看是否触发: ```sql EXPLAIN SELECT * FROM table WHERE a=1 OR b=2; ``` 若输出`type=index_merge`,说明优化器已尝试合并索引[^2]。 --- #### 5. **避免OR与其他复杂条件混用** 若OR与LIKE、范围查询等混合使用,可能使优化失效。例如: ```sql -- 低效查询 SELECT * FROM logs WHERE (timestamp > '2023-01-01' AND event_type='error') OR (message LIKE '%timeout%'); ``` - **优化方案**:拆分查询或使用全文索引替代LIKE。 --- #### 6. **强制索引或优化器提示** 在特定场景下,可通过`FORCE INDEX`提示强制使用索引: ```sql SELECT * FROM table FORCE INDEX(idx_a) WHERE a=1 OR b=2; ``` --- ### 最佳实践总结 1. **优先使用UNION ALL**:拆分OR条件并测试性能差异。 2. **检查执行计划**:通过`EXPLAIN`确认索引使用情况[^2]。 3. **控制结果集大小**:OR条件可能导致结果集膨胀,添加LIMIT或过滤条件。 4. **定期更新统计信息**:帮助优化器更准确选择执行计划[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值