
# 表达式分区
支持指定基本表达式/函数作为分区键，且可通过指定的分区键进行创建和使用分区表，对于创建的分区表，支持分区表中的DDL功能和DML功能，实现查询优化和运维管理。
- 表达式分区仅在M-Compatibility模式数据库中生效。
- 支持在分区表的分区键中使用表达式，数据分布方式根据表达式的计算结果进行分区，其他使用方式和非表达式分区表一致。
- 表达式分区对于一级分区支持范围分区、哈希分区以及列表分区，二级分区支持Range-Hash以及List-Hash分区。
- 表达式分区中分区键的个数仅支持1个。
- 表达式中不支持包含生成列。
- 表达式分区键中支持函数和操作符如[表1]所示。
   表1支持作为表达式分区键的函数和操作符 
  |      函数      |                                      ABS()                                       | CEILING() 对应分区场景下函数返回值类型需要满足该分区的数据类型约束。 |                          DATEDIFF()                           |                            DAY()                             | DAYOFMONTH() | DAYOFWEEK() |
  |      函数      |
  |      函数      |
  |      函数      |
  |---|---|---|---|---|---|---|
  | DAYOFYEAR()  | EXTRACT() 不支持使用week标识符。            | FLOOR() 对应分区场景下函数返回值类型需要满足该分区的数据类型约束。    | HOUR()                                                        | MICROSECOND()                                                | MINUTE()     |
  | MOD()        | MONTH()                                                                          | QUARTER()                                                                               | SECOND()                                                      | TIME_TO_SEC()                                                | TO_DAYS()    |
  | TO_SECONDS() | UNIX_TIMESTAMP() 只支持TIMESTAMP类型的列。 | WEEKDAY()                                                                               | YEAR()                                                        | YEARWEEK()                                                   | -            |
  | **操作符**      | DIV 操作符                                                                          | + 操作符                                                                                   | - 操作符 进行减操作。 | - 操作符 进行取负操作。 | \* 操作符       | -           |
     
  ![](https://support.huaweicloud.com/centralized-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
  表达式的计算结果需要满足对应分区的数据类型约束。例如：CEILING(float)返回值为float类型，该表达式使用在range分区会因为数据类型不支持而报错。CEILING(int)返回值为int类型，则可以直接在range分区使用。
  
#### 示例
- 范围表达式分区，示例如下：
  ```
  -- 通过order_time将订单按时间的月份划分为上半年订单和下半年订单，分别存储到对应的分区中。
  m_db=# CREATE TABLE range_expr_part(order_id INT, order_time DATETIME)
  PARTITION BY RANGE(MONTH(order_time))
  (
     PARTITION p_first_half VALUES LESS THAN(7),
     PARTITION p_second_half VALUES LESS THAN(13)
  );
  m_db=# DROP TABLE range_expr_part;
  ```
  
- 哈希表达式分区，示例如下：
  ```
  -- 通过order_time字段将订单按时间中的年份通过hash计算划分到5个分区，分别存储到对应的分区中。
  m_db=# CREATE TABLE hash_expr_part(order_id INT, order_time DATETIME)
  PARTITION by HASH(YEAR(order_time))
  (
     PARTITION p_0,
     PARTITION p_1,
     PARTITION p_2,
     PARTITION p_3,
     PARTITION p_4
  );
  m_db=# DROP TABLE hash_expr_part;
  ```
  
- 列表表达式分区，示例如下：
  ```
  -- 通过order_time字段将订单按时间中的月份通过根据具体分区枚举信息，将订单划分到4个季度，分别存储到对应的分区中。
  m_db# CREATE TABLE list_expr_part(order_id INT, order_time DATETIME)
  PARTITION BY LIST(MONTH(order_time))
  (
     PARTITION p_first_quarter VALUES (1, 2, 3),
     PARTITION p_second_quarter VALUES (4, 5, 6),
     PARTITION p_third_quarter VALUES (7, 8, 9),
     PARTITION p_fourth_quarter VALUES (10, 11, 12)
  );
  m_db=# DROP TABLE list_expr_part;
  ```
  
- range-hash表达式分区，示例如下：
  ```
  m_db=# CREATE TABLE range_hash_expr_part
  (
      a INT,
      b INT,
      c CHAR(24)
  )
  PARTITION BY RANGE (abs(a)) SUBPARTITION BY HASH (MOD(a, 10))
  (
    PARTITION p50 VALUES LESS THAN(50)
    (
      SUBPARTITION p50_a,
      SUBPARTITION p50_b
    ),
    PARTITION p100 VALUES LESS THAN(100)
    (
      SUBPARTITION p100_a,
      SUBPARTITION p100_b
    )
  );
  m_db=# DROP TABLE range_hash_expr_part;
  ```
  
- list-hash分区表达式分区，示例如下：
  ```
  m_db=# CREATE TABLE list_hash_expr_part
  (
      a INT,
      b DATETIME,
      c CHAR(24)
  )
  PARTITION BY LIST (a DIV 10) SUBPARTITION BY HASH (QUARTER(b))
  (
    PARTITION list_small VALUES (0, 1, 2, 3, 4)
    (
      SUBPARTITION list_small_a,
      SUBPARTITION list_small_b
    ),
    PARTITION list_large VALUES (5, 6, 7, 8, 9)
    (
      SUBPARTITION list_large_a,
      SUBPARTITION list_large_b
    ),
    PARTITION list_default VALUES (default)
    (
      SUBPARTITION list_default_a,
      SUBPARTITION list_default_b
    )
  );
  m_db=# DROP TABLE list_hash_expr_part;
  ```
  
- 表达式分区表静态剪枝，示例如下：
  ```
  -- 在满足剪枝当前已有的条件下，还需要检索条件中的表达式信息和分区键完全匹配，可以满足表达式分区场景下的静态剪枝。
  m_db=# CREATE TABLE expr_part (c1 int, c2 int)
      PARTITION BY RANGE (c1 + 1)
  (
      PARTITION p1 VALUES LESS THAN(10),
      PARTITION p2 VALUES LESS THAN(20),
      PARTITION p3 VALUES LESS THAN(MAXVALUE)
  );
  m_db=# EXPLAIN (VERBOSE ON, COSTS OFF) SELECT * FROM expr_part WHERE c1 + 1 = 1;
                  QUERY PLAN                
  ------------------------------------------
   Partitioned Seq Scan on public.expr_part
     Output: c1, c2
     Filter: ((expr_part.c1 + 1) = 1)
     Selected Partitions:  1
  (4 rows)
  m_db=# DROP TABLE expr_part;
  ```
  
- 表达式分区表动态剪枝，示例如下：
  ```
  -- 在满足剪枝当前已有的条件下，还需要join条件中表达式完全匹配分区键表达式，且索引的信息也与分区键表达式完全相同，可以满足表达式分区场景下的动态剪枝。
  m_db=# CREATE TABLE expr_part1 (c1 int, c2 int)
      PARTITION BY RANGE (c1 + 1)
  (
      PARTITION p1 VALUES LESS THAN(10),
      PARTITION p2 VALUES LESS THAN(20),
      PARTITION p3 VALUES LESS THAN(MAXVALUE)
  );
  CREATE TABLE expr_part2 (c1 INT, c2 INT)
      PARTITION BY RANGE (c1 + 2)
  (
      PARTITION p1 VALUES LESS THAN(10),
      PARTITION p2 VALUES LESS THAN(20),
      PARTITION p3 VALUES LESS THAN(30),
      PARTITION p4 VALUES LESS THAN(MAXVALUE)
  );
  CREATE INDEX idx_expr_part1_c1 ON expr_part1((c1 + 1)) LOCAL;
  m_db=# EXPLAIN (VERBOSE ON, COSTS OFF) SELECT /*+ nestloop(t2 t1) */* FROM expr_part2 t2 JOIN expr_part1 t1 ON (t1.c1 + 1) = (t2.c2 + 2);
                                       QUERY PLAN                                     
  ------------------------------------------------------------------------------------
   Nested Loop
     Output: t2.c1, t2.c2, t1.c1, t1.c2
     ->  Partition Iterator
           Output: t2.c1, t2.c2
           Iterations: 4
           ->  Partitioned Seq Scan on public.expr_part2 t2
                 Output: t2.c1, t2.c2
                 Selected Partitions:  1..4
     ->  Partition Iterator
           Output: t1.c1, t1.c2
           Iterations: PART
           ->  Partitioned Index Scan using idx_expr_part1_c1 on public.expr_part1 t1
                 Output: t1.c1, t1.c2
                 Index Cond: ((t1.c1 + 1) = (t2.c2 + 2))
                 Selected Partitions:  1..3 (ppi-pruning)
  (15 rows)
  m_db=# DROP TABLE expr_part1;
  m_db=# DROP TABLE expr_part2;
  ```
  
 
