
# 范围查询冗余条件消除
#### 操作场景
社区MySQL在处理 WHERE col = const 或 WHERE col \> const 等谓词时，若列上存在索引且优化器选择 Range scan 访问方式，范围扫描的上下界已隐含保证了谓词成立。然而，SQL 层仍会对每一行重复求值，造成不必要的 CPU 开销。对于大范围扫描场景，这种冗余求值的性能损失尤为突出。
以查询 SELECT \* FROM t WHERE a = 5 AND b \> 3 为例，若索引为 (a, b)，范围扫描的 min_key 为 \[5, 3\]、max_key 为 \[5, +∞\]，则 a = 5 已被范围下界完全保证，无需逐行求值。但社区 MySQL 仅支持 REF 访问的等值消除（test_if_ref），对 Range scan 场景下的等值及不等式谓词均不做消除。
理论上，可引入类似 REF 访问的等值消除机制来处理 Range scan，但面临以下挑战：
- 边界信息访问安全：Range scan 的区间边界存储在 QUICK_RANGE 结构中，需安全读取并与谓词常量比较。若比较语义与存储引擎不一致，可能导致谓词被误删，进而产生错误查询结果。
- 合并区间语义丢失：OR 子句产生的合并区间与原始谓词形式可能不一致。例如 c=3 OR c\>2 会被合并为 c\>2 的单区间（无上界），此时无法判断 c=3 是否冗余，强行消除会导致语义错误。
- NDP 下推条件受影响：NDP（Near Data Processing）下推依赖 WHERE 条件，冗余消除后可能导致可下推条件减少，反而降低整体性能。
因此，TaurusDB支持范围查询冗余条件消除功能。当表使用Range scan访问时，优化器自动识别并移除已被范围上下界隐含保证的谓词，减少SQL层的重复求值开销，提升查询性能。
#### 转换过程
**原查询：**
```
SELECT * FROM t WHERE a = 5 AND b > 3
```
若索引为(a, b)，Range scan 的 min_key=\[5, 3\]，max_key=\[5, +∞\]：
- a = 5：min_key 和 max_key 中 a 的值均为5，被范围隐含保证，无需求值，因此被消除。
- b \> 3：min_key 中 b 的值为 3，但范围下界为包含（\>=），谓词为严格大于（\>），两者语义不同，无法消除，即被保留。
**转换后：**
SQL层实际求值的条件（a=5 已被消除）。
```
SELECT * FROM t WHERE b > 3
```
#### 前提条件
- TaurusDB内核版本为2.0.42.230600或以上的版本。内核版本的查询方法请参见[如何查看云数据库 TaurusDB实例的版本号](https://support.huaweicloud.com/taurusdb_faq/taurusdb_faq_0141.html)。
- 参数rds_empty_redundant_check_in_range_scan设置为ON。详细内容请参见[修改TaurusDB实例参数](https://support.huaweicloud.com/usermanual-taurusdb/taurusdb_configuration.html)。
 
#### 支持的谓词类型
表1支持的谓词类型 
| 谓词类型    | SQL 示例               | 消除条件                                    |
|:---|:---|:---|
| 等值      | col = 5              | 范围的min_key和max_key中该列的值均等于常量。           |
| 小于      | col \< 10            | 范围的max_key等于常量，且范围上界为严格小于（NEAR_MAX 标记）。 |
| 小于等于    | col \<= 10           | 范围的max_key等于常量。                         |
| 大于      | col \> 3             | 范围的min_key等于常量，且范围下界为严格大于（NEAR_MIN 标记）。 |
| 大于等于    | col \>= 3            | 范围的min_key等于常量                          |
| BETWEEN | col BETWEEN 3 AND 10 | 范围的min_key 等于下界常量且 max_key 等于上界常量。      |
| IS NULL | col IS NULL          | 范围的min_key和max_key的NULL标记位均为1。          |
   
#### 支持的查询语句
- SELECT
- INSERT ... SELECT
- REPLACE ... SELECT
- 支持视图、PREPARED STMT
- 支持组合索引中的非首列字段
- 支持 DESC 索引
- 支持可空字段的 NULL 字节处理
 
#### 约束限制
- 仅支持简单的Range scan（单区间扫描），不支持 index merge、skip scan。
- 仅支持单区间（ranges.size() == 1），不支持 OR 产生的不相交多区间。
- 不支持前缀索引段（即索引列定义了前缀长度，如col(10)）。
- 不支持BIT类型字段。
- 不支持动态范围扫描（Dynamic Range，即执行时才确定范围的场景）。
- 不支持OR子句内部的谓词消除。OR子句会被临时禁用冗余检查，确保正确性。
- 不支持NOT BETWEEN。
- 不支持外连接中NULL扩展表的谓词消除。
- 当 NDP（Near Data Processing）可能启用时，冗余条件消除自动禁用，优先保证NDP下推效果。
- Hypergraph 优化器的 EXPLAIN 输出尚未适配该特性。
 
#### 使用方法
该特性通过会话参数rds_empty_redundant_check_in_range_scan控制。
表2参数说明 
| 参数                                      | 级别               | 默认值 | 说明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
|:---|:---|:---|:---|
| rds_empty_redundant_check_in_range_scan | SESSION / GLOBAL | OFF | 索引范围扫描时，SQL层去除冗余的条件并对存储引擎返回的行不再执行冗余条件的检查，扩大了Offset Pushdown的生效范围。 - ON : 优化生效，SQL层去除冗余检查。  - OFF : 优化不生效。   |
   
#### 示例
1. 创建测试表并插入数据：
   ```
   CREATE TABLE t1 (a INT, b INT, INDEX(a, b));
   INSERT INTO t1 VALUES (1,2),(2,3),(3,3),(4,3),(5,5),(2,5),(3,7);
   ```
   
2. 关闭冗余条件消除，查看执行计划：
   ```
   SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
   EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;
   ```
   执行计划中保留了Filter层，SQL层需要逐行求值a = 5 和 b \> 3：
   ```
   mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;
   +-----------------------------------------------------------------------------------------------------------------------+
   | EXPLAIN                                                                                                               |
   +-----------------------------------------------------------------------------------------------------------------------+
   | -> Filter: ((t1.a = 5) and (t1.b > 3))  (cost=0.46 rows=1)
       -> Index range scan on t1 using a  (cost=0.46 rows=1)
   +-----------------------------------------------------------------------------------------------------------------------+
   ```
   
3. 开启冗余条件消除，查看执行计划：
   ```
   SET SESSION rds_empty_redundant_check_in_range_scan = ON;
   EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;
   ```
   \`Filter\` 层消失，\`a = 5\` 和 \`b \> 3\` 均已被范围扫描的上下界隐含保证，无需SQL层重复求值：
   ```
   mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;
   +--------------------------------------------------------+
   | EXPLAIN                                                |
   +--------------------------------------------------------+
   | -> Index range scan on t1 using a  (cost=0.46 rows=1)
   +--------------------------------------------------------+
   ```
   
4. 验证查询结果一致性：
   ```
   SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
   SELECT * FROM t1 WHERE a = 2 AND b > 2;
   ```
   ```
   SET SESSION rds_empty_redundant_check_in_range_scan = ON;
   SELECT * FROM t1 WHERE a = 2 AND b > 2;
   ```
   两种模式下查询结果完全一致：
   ```
   +------+------+
   | a    | b    |
   +------+------+
   |    2 |    3 |
   |    2 |    5 |
   +------+------+
   ```
   
 
#### 性能测试
以下测试基于1000万行宽表，组合主键上的等值+范围查询场景，验证冗余条件消除对查询性能的提升。
1. 准备测试数据。
   ```
   SET SESSION cte_max_recursion_depth = 10000001;
   ```
   ```
   DROP TABLE IF EXISTS big_test;
   CREATE TABLE big_test (
   a INT NOT NULL,
   b INT NOT NULL,
   c INT NOT NULL,
   d INT NOT NULL,
   e INT NOT NULL,
   f1 VARCHAR(200),
   f2 VARCHAR(200),
   f3 VARCHAR(200),
   PRIMARY KEY (a, b, c, d, e)
   ) ENGINE=InnoDB ROW_FORMAT=COMPACT;
   ```
   ```
   INSERT INTO big_test (a, b, c, d, e, f1, f2, f3)
   WITH RECURSIVE seq AS (
   SELECT 1 AS n
   UNION ALL
   SELECT n+1 FROM seq WHERE n < 10000000
   )
   SELECT
   (n-1) DIV 2000000 + 1 AS a,
   (n-1) DIV 200000 MOD 10 + 1 AS b,
   (n-1) DIV 20000 MOD 10 + 1 AS c,
   (n-1) DIV 200 MOD 100 + 1 AS d,
   n AS e,
   REPEAT(CONCAT('val', n MOD 1000), 5) AS f1,
   REPEAT(CONCAT('key', n MOD 500), 5) AS f2,
   REPEAT(CONCAT('dat', n MOD 200), 5) AS f3
   FROM seq;
   ```
   
2. 执行计划对比。 查询WHERE a = 3 AND b \> 3，匹配约200万行。其中a = 3可被范围下界隐含保证，属于冗余谓词。
   - 关闭冗余条件消除时，EXPLAIN 显示Using where，SQL层需逐行求值a = 3 AND b \> 3。
     ```
     SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
     ```
     ```
     EXPLAIN SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
     +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------------+
     | id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows    | filtered | Extra       |
     +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------------+
     |  1 | SIMPLE      | big_test | range | PRIMARY       | PRIMARY | 8       | NULL | 2719004 |   100.00 | Using where |
     +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------------+
     ```
     
   
   - 开启冗余条件消除时，a = 3被消除，EXPLAIN 的 Extra 列不再显示 Using where。
     ```
     SET SESSION rds_empty_redundant_check_in_range_scan = ON;
     ```
     ```
     EXPLAIN SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
     +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------+
     | id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows    | filtered | Extra |
     +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------+
     |  1 | SIMPLE      | big_test | range | PRIMARY       | PRIMARY | 8       | NULL | 2719004 |   100.00 | NULL  |
     +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------+
     ```
     
    
3. 查询耗时对比
   ```
   SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
   SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
   ```
   ```
   SET SESSION rds_empty_redundant_check_in_range_scan = ON;
   SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
   ```
   - 冗余条件消除关闭
     ```
     mysql> SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
     +-----------------+
     | SUM(LENGTH(f1)) |
     +-----------------+
     |        41230000 |
     +-----------------+
     1 row in set (0.69 sec)
     ```
     
   
   - 冗余条件消除开启
     ```
     mysql> SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
     +-----------------+
     | SUM(LENGTH(f1)) |
     +-----------------+
     |        41230000 |
     +-----------------+
     1 row in set (0.51 sec)
     ```
     
   
   
   表3查询耗时对比 
   | 模式  | Extra       | 耗时    | 说明                           |
   |:---|:---|:---|:---|
   | OFF | Using where | 0.69s | SQL层对200万行逐行求值 a=3 AND b\>3。 |
   | ON  | NULL        | 0.51s | a=3 被消除，仅求值 b\>3，性能提升约 26%。  |
      
   
 
