问题现象
查询列包含 LOB 类型字段,不会将 OR 条件改写成 UNION ALL,即便指定 USE_CONCAT Hint 也无法生效。
USE_CONCAT Hint 指示优化器使用 UNION ALL 运算符将查询 WHERE 子句中的组合 OR 条件转换为复合查询。如果没有设置 USE_CONCAT Hint,则仅当使用串联查询的成本低于不使用的成本时,才会发生此转换。 测试示例:
创建测试表。
obclient [OBORACLE]> CREATE table t_concat( id number,name varchar2(10), addr cLOB); Query OK, 0 rows affected (0.179 sec)使用存储过程循环插入测试数据。
obclient [OBORACLE]> begin for i in 1..1000 loop insert into t_concat values(i ,'AAA'||i ,'广东省广州市AAA'||i) ; end loop; end; / Query OK, 1 row affected (0.255 sec)提交插入的数据。
obclient [OBORACLE]> commit; Query OK, 0 rows affected (0.017 sec)在字段 id 和 name 上各自创建相应的索引。
obclient [OBORACLE]> create index i_concat_1 on t_concat(id); Query OK, 0 rows affected (1.058 sec)obclient [OBORACLE]> create index i_concat_2 on t_concat(name); Query OK, 0 rows affected (0.857 sec)不查询 LOB 字段,OceanBase 数据库内部自动转换 or 为 union all,可走索引。
展示执行计划。
obclient [OBORACLE]> explain select id,name from t_concat where id=101 or name = 'AAA202'\G;输出结果如下:
*************************** 1. row *************************** Query Plan: ==================================================== |ID|OPERATOR |NAME |EST. ROWS|COST| ---------------------------------------------------- |0 |UNION ALL | |2 |183 | |1 | TABLE SCAN|T_CONCAT(I_CONCAT_1)|1 |92 | |2 | TABLE SCAN|T_CONCAT(I_CONCAT_2)|1 |92 | ==================================================== Outputs & filters ------------------------------------- 0 - output([UNION([1])], [UNION([2])]), filter(nil) 1 - output([T_CONCAT.ID], [T_CONCAT.NAME]), filter(nil), access([T_CONCAT.ID], [T_CONCAT.NAME]), partitions(p0) 2 - output([T_CONCAT.ID], [T_CONCAT.NAME]), filter([lnnvl(cast(T_CONCAT.ID = 101, TINYINT(-1, 0)))]), access([T_CONCAT.ID], [T_CONCAT.NAME]), partitions(p0) 1 row in set (0.009 sec) ERROR: No query specified查询 LOB 字段,OceanBase 数据库无法走 union all,走了全表扫描。
展示执行计划。
obclient [OBORACLE]> explain select id,name,addr from t_concat where id=101 or name = 'AAA202'\G;输出结果如下:
*************************** 1. row *************************** Query Plan: ======================================= |ID|OPERATOR |NAME |EST. ROWS|COST| --------------------------------------- |0 |TABLE SCAN|T_CONCAT|20 |400 | ======================================= Outputs & filters ------------------------------------- 0 - output([T_CONCAT.ID], [T_CONCAT.NAME], [T_CONCAT.ADDR]), filter([T_CONCAT.ID = 101 OR T_CONCAT.NAME = ?]), access([T_CONCAT.ID], [T_CONCAT.NAME], [T_CONCAT.ADDR], [T_CONCAT.__pk_increment]), partitions(p0) 1 row in set (0.013 sec) ERROR: No query specified指定 hint 也无法生效。
展示执行计划。
obclient [OBORACLE]> explain select /*+ USE_CONCAT*/ id,name,addr from t_concat where id=101 or name = 'AAA202'\G;输出结果如下:
*************************** 1. row *************************** Query Plan: ======================================= |ID|OPERATOR |NAME |EST. ROWS|COST| --------------------------------------- |0 |TABLE SCAN|T_CONCAT|20 |400 | ======================================= Outputs & filters ------------------------------------- 0 - output([T_CONCAT.ID], [T_CONCAT.NAME], [T_CONCAT.ADDR]), filter([T_CONCAT.ID = 101 OR T_CONCAT.NAME = ?]), access([T_CONCAT.ID], [T_CONCAT.NAME], [T_CONCAT.ADDR], [T_CONCAT.__pk_increment]), partitions(p0) 1 row in set (0.008 sec) ERROR: No query specified
问题原因
OceanBase 数据库 BUG。当查询中的 SELECT 列里存在 LOB 类型字段时,or-expansion 改写会误被禁止,导致上述查询计划没办法展开成 union all,导致走不上索引。
适用版本
OceanBase 数据库 V2.x、V3.x 版本。
解决方法
解决方法:
升级至问题已修复版本。目前已修复的版本包括 OceanBase 数据库企业版 V2.2.77 BP15 (oceanbase-2.2.77-115000012023010607) 及之后版本、V3.1.2 BP11 (oceanbase-3.1.2-111000052023010412) 及之后版本。
应急处理方法
改写 SQL:
手动改写为 union all。
去掉 select 里面的 LOB 类型字段。