---
title: "TABLE SCAN - OceanBase 数据库 V4.1.0 | OceanBase 文档中心"
description: TABLE SCAN TABLE SCAN 算子是存储层和 SQL 层的接口，用于展示优化器选择哪个索引来访问数据。 在 OceanBase 数据库 V4.1.0 版本中，对于普通索引和全局索引，索引回表的逻辑是封装在 TABLE SCAN 算子中的；在执行计划展示时，会有 is_index_back 标识算子是否需…
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*P8CuR4UJ_FkAAAAAAAAAAAAADiGDAQ/original) OceanBase 数据库分布式版 - V 4.1.0

# TABLE SCAN

更新时间：2023-12-20 14:46:00

[编辑](https://github.com/oceanbase/oceanbase-doc/edit/V4.1.0/zh-CN/700.reference/300.performance-tuning-guide/500.sql-optimization/200.sql-execution-plan/200.execution-plan-operator/100.table-scan.md)  

`TABLE SCAN` 算子是存储层和 SQL 层的接口，用于展示优化器选择哪个索引来访问数据。

在 OceanBase 数据库 V4.1.0 版本中，对于普通索引和全局索引，索引回表的逻辑是封装在 `TABLE SCAN` 算子中的；在执行计划展示时，会有 `is_index_back` 标识算子是否需要回表，以及 `is_global_index` 标识算子是否是扫描全局索引。

示例：含 `TABLE SCAN` 算子的执行计划

```sql
obclient> CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT, c3 INT, c4 INT,
      INDEX k1(c2,c3));
Query OK, 0 rows affected

Q1：
obclient> EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1 = 1\G
*************************** 1. row ***************************
Query Plan:
| ==================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
----------------------------------
|0 |TABLE GET|t1  |1        |53  |
==================================
Outputs & filters:
-------------------------------------
  0 - output([t1.c1(0x7f22fbe69340)], [t1.c2(0x7f22fbe695c0)], [t1.c3(0x7f22fbe69840)], [t1.c4(0x7f22fbe69ac0)]), filter(nil),
      access([t1.c1(0x7f22fbe69340)], [t1.c2(0x7f22fbe695c0)], [t1.c3(0x7f22fbe69840)], [t1.c4(0x7f22fbe69ac0)]), partitions(p0),
      is_index_back=false,
      range_key([t1.c1(0x7f22fbe69340)]), range[1 ; 1],
      range_cond([t1.c1(0x7f22fbe69340) = 1(0x7f22fbe68cf0)])

Q2：
obclient> EXPLAIN EXTENDED SELECT * FROM t1 WHERE c2 < 1 AND c3 < 1 AND
         c4 < 1\G
*************************** 1. row ***************************
Query Plan:
| ======================================
|ID|OPERATOR  |NAME  |EST. ROWS|COST |
--------------------------------------
|0 |TABLE SCAN|t1(k1)|100      |12422|
======================================

Outputs & filters:
-------------------------------------
  0 - output([t1.c1(0x7f22fbd1e220)], [t1.c2(0x7f227decec40)], [t1.c3(0x7f227decf9b0)], [t1.c4(0x7f22fbd1dfa0)]), filter([t1.c3(0x7f227decf9b0) < 1(0x7f227decf360)], [t1.c4(0x7f22fbd1dfa0) < 1(0x7f22fbd1d950)]),
      access([t1.c2(0x7f227decec40)], [t1.c3(0x7f227decf9b0)], [t1.c4(0x7f22fbd1dfa0)], [t1.c1(0x7f22fbd1e220)]), partitions(p0),
      is_index_back=true, filter_before_indexback[true,false],
      range_key([t1.c2(0x7f227decec40)], [t1.c3(0x7f227decf9b0)], [t1.c1(0x7f22fbd1e220)]),
      range(NULL,MAX,MAX ; 1,MIN,MIN),
      range_cond([t1.c2(0x7f227decec40) < 1(0x7f227dece5f0)])

Q3:
obclient> CREATE TABLE t2(c1 INT PRIMARY KEY, c2 INT, c3 INT) PARTITION BY
       HASH(c1) PARTITIONS 4;
Query OK, 0 rows affected
obclient> CREATE INDEX i2 ON t2(c2) GLOBAL;
Query OK, 0 rows affected
obclient> EXPLAIN SELECT * FROM t2 WHERE c2 = 1;
+------------------------------------------------------------------+
| Query Plan                                                       |
+------------------------------------------------------------------+
| ========================================================         |
| |ID|OPERATOR              |NAME  |EST.ROWS|EST.TIME(us)|         |
| --------------------------------------------------------         |
| |0 |DISTRIBUTED TABLE SCAN|t2(i2)|1       |28          |         |
| ========================================================         |
| Outputs & filters:                                               |
| -------------------------------------                            |
|   0 - output([t2.c1], [t2.c2], [t2.c3]), filter(nil), rowset=256 |
|       access([t2.c1], [t2.c2], [t2.c3]), partitions(p0)          |
|       is_index_back=true, is_global_index=true,                  |
|       range_key([t2.c2], [t2.c1]), range(1,MIN ; 1,MAX),         |
|       range_cond([t2.c2 = 1])                                    |
+------------------------------------------------------------------+

O4:
obclient> CREATE TABLE t3(pk INT, c1 INT, c2 INT, PRIMARY KEY(pk));
Query OK, 0 rows affected
obclient> INSERT INTO t3 (pk,c1,c2) SELECT LEVEL, MOD(LEVEL,3), LEVEL FROM DUAL CONNECT BY LEVEL <= 10000;
Query OK, 10000 rows affected
Records: 10000  Duplicates: 0  Warnings: 0
obclient> CREATE INDEX i1 ON t3(c1,c2);
Query OK, 0 rows affected
obclient> EXPLAIN SELECT * FROM t3 WHERE c2 = 1;
+-----------------------------------------------------------------------------------------+
| Query Plan                                                                              |
+-----------------------------------------------------------------------------------------+
| =================================================                                       |
| |ID|OPERATOR       |NAME  |EST.ROWS|EST.TIME(us)|                                       |
| -------------------------------------------------                                       |
| |0 |INDEX SKIP SCAN|T3(I1)|1       |11          |                                       |
| =================================================                                       |
| Outputs & filters:                                                                      |
| -------------------------------------                                                   |
|   0 - output([T3.PK], [T3.C1], [T3.C2]), filter([T3.C2 = 1]), rowset=256                |
|       access([T3.PK], [T3.C2], [T3.C1]), partitions(p0)                                 |
|       is_index_back=false, is_global_index=false, filter_before_indexback[false],       |
|       range_key([T3.C1], [T3.C2], [T3.PK]), range(MIN,MIN,MIN ; MAX,MAX,MAX)always true |
|       prefix_columns_cnt = 1 , skip_scan_range(1,MIN ; 1,MAX)                           |
+-----------------------------------------------------------------------------------------+
12 rows in set

```

上述示例中，执行计划展示中的 `outputs & filters` 详细展示了 `TABLE SCAN` 算子的输出信息如下：

| **信息名称** | **含义** |
| --- | --- |
| operator | `TABLE SCAN` 算子的 `operator` 有三种形式：`TABLE SCAN`、`TABLE GET` 和 `INDEX SKIP SCAN`。   - `TABLE SCAN` 属于范围扫描，会返回 0 行或者多行数据。 - `TABLE GET` 直接用主键定位，返回 0 行或者 1 行数据。 - `INDEX SKIP SCAN` 属于范围扫描，跳跃执行，会返回 0 行或者多行数据。 |
| name | 选择用哪个索引来访问数据。选择的索引的名字会跟在表名后面，如果没有索引的名字，则说明执行的是主表扫描。 这里需要注意，在 OceanBase 数据库中，主表和索引的组织结构是一样的，主表本身也是一个索引。更多信息，可以参考 [索引的存储](https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001701186)。 |
| output | 该算子的输出列。 |
| filter | 该算子的过滤谓词。 由于示例中 Q1 查询的 `TABLE GET` 算子没有设置 `filter`，所以为 `nil`。 |
| partitions | 查询需要扫描的分区。 |
| is_index_back | 该算子是否需要回表。 例如，在 Q1 查询中，因为选择了主表，所以不需要回表。在 Q2 查询中，索引列是 `(c2,c3,c1)`, 由于查询需要返回 `c4` 列，所以需要回表。 |
| is_global_index | 该算子是否是扫描全局索引。例如，在Q2查询中，使用的索引为普通索引，所以`is_global_index` 为 `false` 。在 Q3 查询中，使用了 `t2` 表的 `i2` 索引， `i2` 索引为全局索引，所以 `is_global_index` 为 `true` 。 |
| filter_before_indexback | 与每个 `filter` 对应，表明该 `filter` 是可以直接在索引上进行计算，还是需要索引回表之后才能计算。 例如，在 Q2 查询中，当 `filter` 为 `c3 < 1` 时， 可以直接在索引上计算，能减少回表数量；当 `filter` 为 `c4 < 1` 时，需要回表取出 `c4` 列之后才能计算。 |
| range_key/range/range_cond | - `range_key`：索引的 `rowkey` 列。 - `range`：索引开始扫描和结束扫描的位置。判断是否是全表扫描需要关注 `range` 的范围。例如，对于一个 `rowkey` 有三列的场景，`range(MIN,MIN, MIN ; MAX, MAX, MAX)` 代表的就是真正意义上的全表扫描。 - `range_cond`：决定索引开始扫描和结束扫描位置的相关谓词。 |
| prefix_columns_cnt | 表示 `INDEX SKIP SCAN` 前缀列的个数。 |
| skip_scan_range | 表示 `INDEX SKIP SCAN` 从后缀列抽取的扫描范围。 |

Q4 查询中 `INDEX SKIP SCAN` 可以在满足一定条件下利用已有的索引来加速扫描。OceanBase 数据库在满足如下限制条件时会尝试选择 `INDEX SKIP SCAN`：

- 表上有收集过统计信息。
 - 查询条件中包含联合索引的后缀列，并且不是其他索引最左前缀。
 - 优化器比较 Index Skip Scan 和全表扫描的代价，发现 Index Skip Scan 代价更低。

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
