DML
ROWID系统列支持在DML语句中使用,包括INSERT/SELECT/UPDATE/DELETE语句,示例如下:
gaussdb=# CREATE TABLE test(a INT, b INT) WITH (hasrowid);
CREATE TABLE
gaussdb=# -- INSERT
gaussdb=# INSERT INTO test values(1, 1);
INSERT 0 1
gaussdb=# -- ROWID表插入语句行记录会携带ROWID系统列
gaussdb=# SELECT ROWID FROM test;
rowid
-------------------------------
AAAAAAAAAP//AAAEGAAAAAAAAAAAB
(1 row)
gaussdb=# -- SELECT,使用ROWID系统列作为WHERE条件
gaussdb=# SELECT * FROM test WHERE ROWID = 'AAAAAAAAAP//AAAEGAAAAAAAAAAAB';
a | b
---+---
1 | 1
(1 row)
gaussdb=# -- UPDATE,使用ROWID系统列作为WHERE条件
gaussdb=# UPDATE test SET b = 2 WHERE ROWID = 'AAAAAAAAAP//AAAEGAAAAAAAAAAAB';
UPDATE 1
gaussdb=# -- DELETE,使用ROWID系统列作为WHERE条件
gaussdb=# DELETE FROM test WHERE ROWID = 'AAAAAAAAAP//AAAEGAAAAAAAAAAAB';
DELETE 1
gaussdb=# -- CLEAN
gaussdb=# DROP TABLE test;
DROP TABLE 在DML语句中使用ROWID系统列时,若打开GUC参数opt_behavior_knob的ROWID_OPT选项(详情请参见《参考》中“数据库运行参数说明 > GUC参数说明 > 查询规划 > 其他优化器选项”章节内容),则支持ROWID系统列谓词条件改写、ROWID系统列ORDER BY语句改写、ROWID系统列max/min优化、ROWID系统列分区剪枝查询优化场景。
ROWID系统列谓词条件改写
- ROWID系统列谓词条件改写
当ROWID系统列用于WHERE子句或JOIN ON子句的谓词条件判断时,可进行条件语句的改写,当前仅支持比较操作符(=、<>、<、>、<=、>=)作为谓词条件的语句改写。
- 当操作符为等号(=)时,支持全场景改写,形如“ROWID = src_expr”的谓词条件(当src_expr也为ROWID系统列时,不进行改写,无实际意义),改写规则如下:
tableoid = ROWID_TABLEOID(src_expr) AND rowno = ROWID_SEQUENCE(src_expr)
改写后两个条件分别触发分区剪枝和索引扫描以提升扫描性能, 对于不可改写的表达式,比如IN表达式“rowid IN ($1,$2,...)”,会进行全表扫描,遍历每行元组进行条件检查,此时不进行剪枝和索引扫描。
- 当操作符为非等号(<>、<、>、<=、>=)时,仅支持部分场景改写,比如范围表达式“ROWID > src_expr”。由于ROWID的比较需综合考虑内部字段(node_slice、bucket_id、table_oid、row_no),改写仅支持普通表且src_expr表达式为可计算常量条件的场景。此时ROWID除了row_no以外的字段完全相同,ROWID到rowno系统列的映射严格保序,形如“ROWID >/</<>/>=/<= src_expr”的谓词条件,会在检查其他字段相同的情况下(对于src_expr的table_oid不同于当前表的场景,由于src_expr为常量条件,可在改写前直接提前计算谓词条件结果,若结果为TRUE,改写为ROWID IS NOT NULL条件,若结果为FALSE,则直接拼接布尔条件FALSE),按如下规则进行改写:
rowno >/</<>/>=/<= ROWID_SEQUENCE(src_expr)
对于无法改写的场景,仍执行全表扫描。
ROWID系统列谓词条件改写遵从如下规格约束:
- WHERE/JOIN ON条件中,当操作符两侧都为ROWID系统列时不改写。
- 形如ROWID IN/NOT IN (xxx)场景,当右侧表达式为子查询表达式、自定义类型表达式、数组表达式时不支持改写。
- 形如NULLIF(ROWID,xxx)或NULLIF(ROWID,xxx)的NULLIF表达式不改写(ROWID = NULLIF(xxx,xxx)可以正常改写)。
- DISTINCT FROM表达式不改写。
- HAVING子句不改写。
- 操作符左右两侧均为RECORD类型表达式时不支持改写。
- ALL/ANY/SOME表达式不支持改写,且形如ROWID = ALL/ANY/SOME (xxx)或ALL/ANY/SOME (xxx)= ROWID的判断条件不支持改写。
示例:
由于不同场景下tableoid字段值不同,以下示例仅作参考,执行时以实际输出结果为准。
以GUC参数explain_perf_mode设置为normal为例。
-- 数据准备 gaussdb=# DROP TABLE IF EXISTS t1; DROP TABLE gaussdb=# CREATE TABLE t1(a int, c int) with (storage_type=astore, hasrowid=ON); CREATE TABLE gaussdb=# INSERT INTO t1 VALUES( 1, 1); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES( 2, 2); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES(3, 3); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES(3, 4); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES(1, 5); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES( 2, 6); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES(3, 7); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES( 3, 8); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES( 1, 9); INSERT 0 1 gaussdb=# INSERT INTO t1 VALUES( 2, 10); INSERT 0 1 gaussdb=# --IMMUTABLE func gaussdb=# CREATE OR REPLACE FUNCTION func RETURN rowid IMMUTABLE AS gaussdb$# temp rowid; gaussdb$# res int; gaussdb$# begin gaussdb$# SELECT rowid INTO temp FROM t1 ORDER BY rowid LIMIT 1; gaussdb$# RETURN temp; gaussdb$# end; gaussdb$# / CREATE FUNCTION gaussdb=# -- WHERE子句 gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM t1 WHERE rowid = func(); QUERY PLAN ---------------------------------------------------------------------- [Parameterized] Index Scan using t1_rowno_idx ON public.t1 Output: a, c, (Expression Flatten Optimized) Index Cond: (t1.rowno = 1::bigint), (Expression Flatten Optimized) Filter: (t1.tableoid = 16760::oid), (Expression Flatten Optimized) (5 rows) gaussdb=# SELECT * FROM t1 WHERE rowid = func(); a | c ---+--- 1 | 1 (1 row) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM t1 WHERE func() = rowid; QUERY PLAN ---------------------------------------------------------------------- [Parameterized] Index Scan using t1_rowno_idx ON public.t1 Output: a, c, (Expression Flatten Optimized) Index Cond: (t1.rowno = 1::bigint), (Expression Flatten Optimized) Filter: (t1.tableoid = 16760::oid), (Expression Flatten Optimized) (5 rows) gaussdb=# SELECT * FROM t1 WHERE func() = rowid; a | c ---+--- 1 | 1 (1 row) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM t1 WHERE rowid = rowid; QUERY PLAN ----------------------------------------------------------------- [Parameterized] Seq Scan ON public.t1 Output: a, c, (Expression Flatten Optimized) Filter: (t1.rowid = t1.rowid), (Expression Flatten Optimized) (4 rows) gaussdb=# SELECT * FROM t1 WHERE rowid = rowid; a | c ---+---- 1 | 1 2 | 2 3 | 3 3 | 4 1 | 5 2 | 6 3 | 7 3 | 8 1 | 9 2 | 10 (10 rows) gaussdb=# --HAVING子句,不支持改写 gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT MAX(a),MAX(rowid) FROM t1 GROUP BY rowid HAVING rowid = 'AAAAAAAAAP//BBBBBAAAAAAAAAAAB'; QUERY PLAN ----------------------------------------------------------------------------------------------------- [Parameterized] GroupAggregate Output: MAX(a), MAX(rowid), rowid, (Expression Flatten Optimized) Group By Key: t1.rowid -> Seq Scan ON public.t1 Output: rowid, a, (Expression Flatten Optimized) Filter: (t1.rowid = 'AAAAAAAAAP//BBBBBAAAAAAAAAAAB'::rowid), (Expression Flatten Optimized) (7 rows) gaussdb=# SELECT MAX(a),MAX(rowid) FROM t1 GROUP BY rowid HAVING rowid = 'AAAAAAAAAP//BBBBBAAAAAAAAAAAB'; MAX | MAX -----+----- (0 rows) gaussdb=# SELECT MAX(a) AS newcol1,rowid_sequence(MAX(rowid)) AS newcol2 FROM t1 GROUP BY rowid ORDER BY newcol1,newcol2; newcol1 | newcol2 ---------+--------- 1 | 1 1 | 5 1 | 9 2 | 2 2 | 6 2 | 10 3 | 3 3 | 4 3 | 7 3 | 8 (10 rows) gaussdb=# -- NULLIF表达式本身不改写,rowid = NULLIF(xxx)改写 gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT rowid FROM t1 WHERE rowid = NULLIF(rowid, rowid); QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------------- [Parameterized] Seq Scan ON public.t1 Output: rowid, (Expression Flatten Optimized) Filter: ((t1.tableoid = rowid_tableoid(NULLIF(t1.rowid, t1.rowid))) AND (t1.rowno = rowid_sequence(NULLIF(t1.rowid, t1.rowid)))), (Expression Flatten Optimized) (4 rows) gaussdb=# SELECT rowid FROM t1 WHERE rowid = NULLIF(rowid, rowid); rowid ------- (0 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT rowid FROM t1 WHERE rowid = NULLIF(rowid, 'AAAAAAAAAP//AAAAAAAAAAAAAAAAA'); QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [Parameterized] Seq Scan ON public.t1 Output: rowid, (Expression Flatten Optimized) Filter: ((t1.tableoid = rowid_tableoid(NULLIF(t1.rowid, 'AAAAAAAAAP//AAAAAAAAAAAAAAAAA'::rowid))) AND (t1.rowno = rowid_sequence(NULLIF(t1.rowid, 'AAAAAAAAAP//AAAAAAAAAAAAAAAAA'::rowid)))), (Expression Flatten Optimized) (4 rows) gaussdb=# SELECT rowid FROM t1 WHERE rowid = NULLIF(rowid, 'AAAAAAAAAP//AAAAAAAAAAAAAAAAA'); rowid ------------------------------- AAAAAAAAAP//AAAEF4AAAAAAAAAAB AAAAAAAAAP//AAAEF4AAAAAAAAAAC AAAAAAAAAP//AAAEF4AAAAAAAAAAD AAAAAAAAAP//AAAEF4AAAAAAAAAAE AAAAAAAAAP//AAAEF4AAAAAAAAAAF AAAAAAAAAP//AAAEF4AAAAAAAAAAG AAAAAAAAAP//AAAEF4AAAAAAAAAAH AAAAAAAAAP//AAAEF4AAAAAAAAAAI AAAAAAAAAP//AAAEF4AAAAAAAAAAJ AAAAAAAAAP//AAAEF4AAAAAAAAAAK (10 rows) gaussdb=# -- rowexpr不改写 gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT a FROM t1 WHERE (rowid,c) IN (SELECT rowid,c FROM t1); QUERY PLAN -------------------------------------------------------------------------------------------------------------------- [Parameterized] Hash Join Output: public.t1.a, (Expression Flatten Optimized) Inner Unique: true Hash Cond: ((public.t1.rowid = public.t1.rowid) AND (public.t1.c = public.t1.c)), (Expression Flatten Optimized) -> Seq Scan ON public.t1 Output: public.t1.a, public.t1.rowid, public.t1.c, (Expression Flatten Optimized) -> Hash Output: public.t1.rowid, public.t1.c -> HashAggregate Output: public.t1.rowid, public.t1.c, (Expression Flatten Optimized) Group By Key: public.t1.rowid, public.t1.c -> Seq Scan ON public.t1 Output: public.t1.rowid, public.t1.c, (Expression Flatten Optimized) (14 rows) gaussdb=# SELECT a FROM t1 WHERE (rowid,c) IN (SELECT rowid,c FROM t1); a --- 1 2 3 3 1 2 3 3 1 2 (10 rows) gaussdb=# CREATE TYPE t1_type AS (rowid rowid, c int); CREATE TYPE gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT a FROM t1 WHERE (rowid, c) IN t1_type(func(), 1); QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- [Parameterized] Seq Scan ON public.t1 Output: a, (Expression Flatten Optimized) Filter Unordered: ((t1.rowid = 'AAAAAAAAAP//AAAEF4AAAAAAAAAAB'::rowid) AND (t1.c = 1)), (Expression Flatten Optimized) Filter Reordered: ((t1.c = 1) AND (t1.rowid = 'AAAAAAAAAP//AAAEF4AAAAAAAAAAB'::rowid)), (Expression Flatten Optimized) (5 rows) gaussdb=# SELECT a FROM t1 WHERE (rowid, c) IN t1_type(func(), 1); a --- 1 (1 row) gaussdb=# -- IN/NOT IN gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT rowid FROM t1 WHERE rowid IN func(); --改写 QUERY PLAN ---------------------------------------------------------------------- [Parameterized] Index Scan using t1_rowno_idx ON public.t1 Output: rowid, (Expression Flatten Optimized) Index Cond: (t1.rowno = 1::bigint), (Expression Flatten Optimized) Filter: (t1.tableoid = 16760::oid), (Expression Flatten Optimized) (5 rows) gaussdb=# SELECT rowid FROM t1 WHERE rowid IN func(); rowid ------------------------------- AAAAAAAAAP//AAAEF4AAAAAAAAAAB (1 row) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT rowid FROM t1 WHERE rowid NOT IN func(); --改写 QUERY PLAN ------------------------------------------------------------------- [Parameterized] Seq Scan ON public.t1 Output: rowid, (Expression Flatten Optimized) Filter: (t1.rowno <> 1::bigint), (Expression Flatten Optimized) (4 rows) gaussdb=# SELECT rowid FROM t1 WHERE rowid NOT IN func(); rowid ------------------------------- AAAAAAAAAP//AAAEF4AAAAAAAAAAC AAAAAAAAAP//AAAEF4AAAAAAAAAAD AAAAAAAAAP//AAAEF4AAAAAAAAAAE AAAAAAAAAP//AAAEF4AAAAAAAAAAF AAAAAAAAAP//AAAEF4AAAAAAAAAAG AAAAAAAAAP//AAAEF4AAAAAAAAAAH AAAAAAAAAP//AAAEF4AAAAAAAAAAI AAAAAAAAAP//AAAEF4AAAAAAAAAAJ AAAAAAAAAP//AAAEF4AAAAAAAAAAK (9 rows) gaussdb=# -- distinct FROM不改写 gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT a FROM t1 WHERE rowid is distinct FROM func(); QUERY PLAN -------------------------------------------------------------------------------------------------------------- [Parameterized] Seq Scan ON public.t1 Output: a, (Expression Flatten Optimized) Filter: (t1.rowid IS DISTINCT FROM 'AAAAAAAAAP//AAAEF4AAAAAAAAAAB'::rowid), (Expression Flatten Optimized) (4 rows) gaussdb=# SELECT a FROM t1 WHERE rowid is distinct FROM func(); a --- 2 3 3 1 2 3 3 1 2 (9 rows) -- 非等号改写示例 gaussdb=# SET cursor_sharing=off; -- 关闭自动参数化 SET gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *, rowid FROM t1 WHERE rowid <> 'AAAAAAAAAP//AAAAAAAAAAAAAAAAA'; QUERY PLAN ------------------------------------------------------- Seq Scan ON public.t1 Output: a, c, rowid, (Expression Flatten Optimized) (2 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *, rowid FROM t1 WHERE rowid <> 'AAAAAAAAAP//AAAEFaAAAAAAAAAAA'; QUERY PLAN ------------------------------------------------------- Seq Scan ON public.t1 Output: a, c, rowid, (Expression Flatten Optimized) (2 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *, rowid FROM t1 WHERE 'AAAAAAAAAP//AAAAAAAAAAAAAAAAA' <> rowid; QUERY PLAN ------------------------------------------------------- Seq Scan ON public.t1 Output: a, c, rowid, (Expression Flatten Optimized) (2 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *, rowid FROM t1 WHERE 'AAAAAAAAAP//AAAEFaAAAAAAAAAAA' <> rowid; QUERY PLAN ------------------------------------------------------- Seq Scan ON public.t1 Output: a, c, rowid, (Expression Flatten Optimized) (2 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *, rowid FROM t1 WHERE rowid <> func(); QUERY PLAN ------------------------------------------------------------------- Seq Scan ON public.t1 Output: a, c, rowid, (Expression Flatten Optimized) Filter: (t1.rowno <> 1::bigint), (Expression Flatten Optimized) (3 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *, rowid FROM t1 WHERE rowid <> func() ORDER BY rowid; QUERY PLAN ------------------------------------------------------------------------ Index Scan using t1_rowno_idx ON public.t1 Output: a, c, rowid, tableoid, rowno, (Expression Flatten Optimized) Filter: (t1.rowno <> 1::bigint), (Expression Flatten Optimized) (3 rows) gaussdb=# RESET cursor_sharing; -- 恢复自动参数化 RESET -- 在JOIN ON子句的谓词条件改写 gaussdb=# CREATE TABLE t2(a int, c text) with (storage_type=astore); CREATE TABLE gaussdb=# gaussdb=# INSERT INTO t2 VALUES( 1, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES( 2, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES(3, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES(3, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES(1, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES( 2, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES(3, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES( 3, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES( 1, func()); INSERT 0 1 gaussdb=# INSERT INTO t2 VALUES( 2, func()); INSERT 0 1 gaussdb=# SELECT * FROM t2; a | c ---+------------------------------- 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB (10 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *,t1.rowid FROM t1 INNER JOIN t2 ON t1.rowid = t2.c; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------- [Parameterized] Hash Join Output: t1.a, t1.c, t2.a, t2.c, t1.rowid, (Expression Flatten Optimized) Inner Unique: true Hash Cond: ((rowid_tableoid((t2.c)::rowid) = t1.tableoid) AND (rowid_sequence((t2.c)::rowid) = t1.rowno)), (Expression Flatten Optimized) -> Seq Scan ON public.t2 Output: t2.a, t2.c -> Hash Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno -> Seq Scan ON public.t1 Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno, (Expression Flatten Optimized) (11 rows) gaussdb=# SELECT *,t1.rowid FROM t1 INNER JOIN t2 ON t1.rowid = t2.c; a | c | a | c | rowid ---+---+---+-------------------------------+------------------------------- 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB (10 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *,t1.rowid FROM t1 LEFT JOIN t2 ON t1.rowid = t2.c; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------- [Parameterized] Hash Right Join Output: t1.a, t1.c, t2.a, t2.c, t1.rowid, (Expression Flatten Optimized) Inner Unique: true Hash Cond: ((rowid_tableoid((t2.c)::rowid) = t1.tableoid) AND (rowid_sequence((t2.c)::rowid) = t1.rowno)), (Expression Flatten Optimized) -> Seq Scan ON public.t2 Output: t2.a, t2.c -> Hash Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno -> Seq Scan ON public.t1 Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno, (Expression Flatten Optimized) (11 rows) gaussdb=# SELECT *,t1.rowid FROM t1 LEFT JOIN t2 ON t1.rowid = t2.c; a | c | a | c | rowid ---+----+---+-------------------------------+------------------------------- 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 3 | 4 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAE 2 | 10 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAK 1 | 9 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAJ 3 | 8 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAI 3 | 3 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAD 1 | 5 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAF 2 | 2 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAC 3 | 7 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAH 2 | 6 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAG (19 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *,t1.rowid FROM t1 RIGHT JOIN t2 ON t1.rowid = t2.c; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------- [Parameterized] Hash Left Join Output: t1.a, t1.c, t2.a, t2.c, t1.rowid, (Expression Flatten Optimized) Inner Unique: true Hash Cond: ((rowid_tableoid((t2.c)::rowid) = t1.tableoid) AND (rowid_sequence((t2.c)::rowid) = t1.rowno)), (Expression Flatten Optimized) -> Seq Scan ON public.t2 Output: t2.a, t2.c -> Hash Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno -> Seq Scan ON public.t1 Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno, (Expression Flatten Optimized) (11 rows) gaussdb=# SELECT *,t1.rowid FROM t1 RIGHT JOIN t2 ON t1.rowid = t2.c; a | c | a | c | rowid ---+---+---+-------------------------------+------------------------------- 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB (10 rows) gaussdb=# EXPLAIN (COSTS OFF, VERBOSE) SELECT *,t1.rowid FROM t1 full JOIN t2 ON t1.rowid = t2.c; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------- [Parameterized] Hash Full Join Output: t1.a, t1.c, t2.a, t2.c, t1.rowid, (Expression Flatten Optimized) Inner Unique: true Hash Cond: ((rowid_tableoid((t2.c)::rowid) = t1.tableoid) AND (rowid_sequence((t2.c)::rowid) = t1.rowno)), (Expression Flatten Optimized) -> Seq Scan ON public.t2 Output: t2.a, t2.c -> Hash Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno -> Seq Scan ON public.t1 Output: t1.a, t1.c, t1.rowid, t1.tableoid, t1.rowno, (Expression Flatten Optimized) (11 rows) gaussdb=# SELECT *,t1.rowid FROM t1 full JOIN t2 ON t1.rowid = t2.c; a | c | a | c | rowid ---+----+---+-------------------------------+------------------------------- 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 3 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 1 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 1 | 1 | 2 | AAAAAAAAAP//AAAEF4AAAAAAAAAAB | AAAAAAAAAP//AAAEF4AAAAAAAAAAB 3 | 4 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAE 2 | 10 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAK 1 | 9 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAJ 3 | 8 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAI 3 | 3 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAD 1 | 5 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAF 2 | 2 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAC 3 | 7 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAH 2 | 6 | | | AAAAAAAAAP//AAAEF4AAAAAAAAAAG (19 rows) -- 清理数据 gaussdb=# DROP TABLE t1; DROP TABLE gaussdb=# DROP TABLE t2; DROP TABLE gaussdb=# DROP FUNCTION func; DROP FUNCTION - 当操作符为等号(=)时,支持全场景改写,形如“ROWID = src_expr”的谓词条件(当src_expr也为ROWID系统列时,不进行改写,无实际意义),改写规则如下:
ROWID系统列ORDER BY语句改写
对于ORDER BY rowid的场景,可改写为ORDER BY tableoid, rowno,如果为普通表或者剪枝到单分区的场景,可进一步简化为ORDER BY rowno。rowid系统列排序规则是按照其包含的字段node_slice、table_oid、bucket_id、row_no依次比较排序。但rowid系统列本身并未建立索引,而rowno字段建立了唯一索引,因此对于普通表场景,ORDER BY rowid实际等效于ORDER BY rowno系统列,而rowno系统列上具有索引,使用上更高效,在两者排序等价场景下将ORDER BY rowid改写为ORDER BY rowno。
ROWID系统列ORDER BY语句改写遵从如下规格约束:
- 普通表ORDER BY rowid单列场景改写为ORDER BY rowno。
- 分区表ORDER BY rowid单列场景改写为ORDER BY tableoid, rowno,若剪枝到单分区,则进一步改写为ORDER BY rowno。
- 普通表或者分区表剪枝到单分区场景,ORDER BY多列且其中一列为tableoid系统列时,支持去除冗余的ORDER BY tableoid(详情见下文介绍)。
- ORDER BY多列时,不支持ORDER BY rowid改写。
- 语句包含GROUP、DISTINCT时,不支持ORDER BY rowid改写。
- 聚集函数中的ORDER BY rowid不支持改写。
- 仅基表为物理表的支持改写,基表为子查询或CTE等不支持改写。
除ORDER BY rowid改写场景外,还支持对冗余tableoid系统列条件进行消除(包括用户添加的tableoid判断条件),满足以下条件时支持去除tableoid排序:
- 排序键大于或等于2个。
- 普通表或者分区表剪枝到单分区。
示例:
由于不同场景下tableoid字段值不同,以下示例仅作参考,执行时以实际输出结果为准。
以GUC参数explain_perf_mode设置为normal为例。
-- 普通表
gaussdb=# CREATE TABLE t1 (
c1 INTEGER,
c2 INTEGER,
c3 INTEGER
) with (storage_type = astore, hasrowid);
CREATE TABLE
gaussdb=#
gaussdb=# INSERT INTO t1 VALUES(generate_series(1,100),generate_series(1,200),generate_series(1,400));
INSERT 0 400
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ * FROM t1 ORDER BY rowid;
QUERY PLAN
------------------------------------------------------------------------------
[Bypass]
[Parameterized]
Index Scan using t1_rowno_idx on public.t1
Output: c1, c2, c3, rowid, tableoid, rowno, (Expression Flatten Optimized)
(4 rows)
gaussdb=# SELECT /*+ indexscan(t1) */ * FROM t1 ORDER BY rowid LIMIT 1;
c1 | c2 | c3
----+----+----
1 | 1 | 1
(1 row)
gaussdb=# SELECT /*+ indexscan(t1) */ * FROM t1 WHERE c1 = 50 ORDER BY rowid;
c1 | c2 | c3
----+-----+-----
50 | 50 | 50
50 | 150 | 150
50 | 50 | 250
50 | 150 | 350
(4 rows)
gaussdb=# SELECT /*+ indexscan(t1) */ * FROM t1 WHERE c2 = 50 ORDER BY rowid;
c1 | c2 | c3
----+----+-----
50 | 50 | 50
50 | 50 | 250
(2 rows)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid,c1 FROM t1 WHERE c1 = 50 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------------
[Parameterized]
Index Scan using t1_rowno_idx on public.t1
Output: rowid, c1, tableoid, rowno, (Expression Flatten Optimized)
Filter: (t1.c1 = 50), (Expression Flatten Optimized)
(4 rows)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid,rowno FROM t1 ORDER BY rowid;
QUERY PLAN
-------------------------------------------------------------------------
[Parameterized]
Index Scan using t1_rowno_idx on public.t1
Output: rowid, rowno, tableoid, rowno, (Expression Flatten Optimized)
(3 rows)
-- ORDER BY多列场景
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid,rowno FROM t1 ORDER BY rowid,c1;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
------------------------------------------------------------------
[Parameterized]
Sort
Output: rowid, rowno, c1
Sort Key: t1.rowid, t1.c1
-> Seq Scan on public.t1
Output: rowid, rowno, c1, (Expression Flatten Optimized)
(6 rows)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid,rowno FROM t1 ORDER BY c1,rowid;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
------------------------------------------------------------------
[Parameterized]
Sort
Output: rowid, rowno, c1
Sort Key: t1.c1, t1.rowid
-> Seq Scan on public.t1
Output: rowid, rowno, c1, (Expression Flatten Optimized)
(6 rows)
-- 分区表
gaussdb=# CREATE TABLE t1_p (
gaussdb(# c1 INTEGER,
gaussdb(# c2 INTEGER,
gaussdb(# c3 INTEGER
gaussdb(# ) with (storage_type = astore, hasrowid)
gaussdb-# PARTITION BY RANGE (c1)
gaussdb-# (
gaussdb(# PARTITION p2023q1 VALUES LESS THAN (30),
gaussdb(# PARTITION p2023q2 VALUES LESS THAN (60),
gaussdb(# PARTITION p2023q3 VALUES LESS THAN (MAXVALUE)
gaussdb(# );
CREATE TABLE
gaussdb=# INSERT INTO t1_p VALUES(generate_series(1,100),generate_series(1,200),generate_series(1,400));
INSERT 0 400
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1_p) */ * FROM t1_p ORDER BY rowid;
WARNING: unused hint: IndexScan(t1_p)
QUERY PLAN
------------------------------------------------------------------------------------------
[Parameterized]
Sort
Output: c1, c2, c3, rowid, tableoid, rowno
Sort Key: t1_p.tableoid, t1_p.rowno
-> Partition Iterator
Output: c1, c2, c3, rowid, tableoid, rowno
Iterations: 3
-> Partitioned Seq Scan on public.t1_p
Output: c1, c2, c3, rowid, tableoid, rowno, (Expression Flatten Optimized)
Selected Partitions: 1..3
(10 rows)
gaussdb=# SELECT /*+ indexscan(t1_p) */ * FROM t1_p ORDER BY rowid LIMIT 1;
c1 | c2 | c3
----+----+----
1 | 1 | 1
(1 row)
-- 静态剪枝
aussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1_p) */ * FROM t1_p WHERE c1 = 90 ORDER BY rowid;
QUERY PLAN
------------------------------------------------------------------------------
[Parameterized]
Partitioned Index Scan using t1_p_rowno_idx on public.t1_p
Output: c1, c2, c3, rowid, tableoid, rowno, (Expression Flatten Optimized)
Filter: (t1_p.c1 = 90), (Expression Flatten Optimized)
Selected Partitions: 3
(5 rows)
gaussdb=# SELECT /*+ indexscan(t1_p) */ * FROM t1_p WHERE c1 = 90 ORDER BY rowid;
c1 | c2 | c3
----+-----+-----
90 | 90 | 90
90 | 190 | 190
90 | 90 | 290
90 | 190 | 390
(4 rows)
-- 动态剪枝
gaussdb=# SET plan_cache_mode = force_generic_plan;
SET
gaussdb=# PREPARE p2(int) AS SELECT /*+ indexscan(t1_p) */ * FROM t1_p WHERE c1 = $1 ORDER BY rowid;
PREPARE
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) EXECUTE p2(10);
QUERY PLAN
------------------------------------------------------------------------------
Partitioned Index Scan using t1_p_rowno_idx on public.t1_p
Output: c1, c2, c3, rowid, tableoid, rowno, (Expression Flatten Optimized)
Filter: (t1_p.c1 = $1), (Expression Flatten Optimized)
Selected Partitions: 1 (pbe-pruning)
(4 rows)
gaussdb=# EXECUTE p2(10);
c1 | c2 | c3
----+-----+-----
10 | 10 | 10
10 | 110 | 110
10 | 10 | 210
10 | 110 | 310
(4 rows)
gaussdb=# RESET plan_cache_mode;
RESET
-- CTE不改写场景
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) WITH cte AS (SELECT rowid,* FROM t1) SELECT * FROM cte ORDER BY rowid;
QUERY PLAN
-------------------------------------------------------------------------------
[Parameterized]
Sort
Output: t1.rowid, t1.c1, t1.c2, t1.c3
Sort Key: t1.rowid
-> Seq Scan on public.t1
Output: t1.rowid, t1.c1, t1.c2, t1.c3, (Expression Flatten Optimized)
(6 rows)
-- 子查询不改写场景
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM (SELECT /*+ indexscan(t1) */ rowid,* FROM t1) ORDER BY rowid;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
-------------------------------------------------------------------------------
[Parameterized]
Sort
Output: t1.rowid, t1.c1, t1.c2, t1.c3
Sort Key: t1.rowid
-> Seq Scan on public.t1
Output: t1.rowid, t1.c1, t1.c2, t1.c3, (Expression Flatten Optimized)
(6 rows)
-- 聚集函数不改写场景
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ max(rowid) FROM t1 ORDER BY rowid;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
------------------------------------------------------------------------------------------
[Parameterized]
Result
Output: $0, (Expression Flatten Optimized)
InitPlan 1 (returns $0)
-> Limit
Output: public.t1.rowid, public.t1.rowno
-> Index Scan Backward using t1_rowno_idx on public.t1
Output: public.t1.rowid, public.t1.rowno, (Expression Flatten Optimized)
(8 rows)
-- GROUP不改写场景
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ count(*), rowid AS min_rowid FROM t1 group BY rowid ORDER BY rowid;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
-----------------------------------------------------------------
[Parameterized]
Sort
Output: (count(*)), rowid
Sort Key: t1.rowid
-> HashAggregate
Output: count(*), rowid, (Expression Flatten Optimized)
Group By Key: t1.rowid
-> Seq Scan on public.t1
Output: rowid, (Expression Flatten Optimized)
(9 rows)
-- DISTINCT不改写场景
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ distinct rowid FROM t1 ORDER BY rowid;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
-------------------------------------------------------------
[Parameterized]
Sort
Output: rowid
Sort Key: t1.rowid
-> HashAggregate
Output: rowid, (Expression Flatten Optimized)
Group By Key: t1.rowid
-> Seq Scan on public.t1
Output: rowid, (Expression Flatten Optimized)
(9 rows)
-- tableoid排序去除
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid,rowno FROM t1 ORDER BY tableoid,c1;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
----------------------------------------------------------------------------
[Parameterized]
Sort
Output: rowid, rowno, tableoid, c1
Sort Key: t1.c1
-> Seq Scan on public.t1
Output: rowid, rowno, tableoid, c1, (Expression Flatten Optimized)
(6 rows)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1_p) */ * FROM t1_p WHERE c1 = 90 ORDER BY tableoid,c2;
WARNING: unused hint: IndexScan(t1_p)
QUERY PLAN
----------------------------------------------------------------------
[Parameterized]
Sort
Output: c1, c2, c3, tableoid
Sort Key: t1_p.c2
-> Partitioned Seq Scan on public.t1_p
Output: c1, c2, c3, tableoid, (Expression Flatten Optimized)
Filter: (t1_p.c1 = 90), (Expression Flatten Optimized)
Selected Partitions: 3
(8 rows)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM (SELECT /*+ indexscan(t1) */ rowid,* FROM t1 ORDER BY c2,tableoid);
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
[Parameterized]
Subquery Scan on __unnamed_subquery__
Output: __unnamed_subquery__.rowid, __unnamed_subquery__.c1, __unnamed_subquery__.c2, __unnamed_subquery__.c3, (Expression Flatten Optimized)
-> Sort
Output: t1.rowid, t1.c1, t1.c2, t1.c3, t1.tableoid
Sort Key: t1.c2
-> Seq Scan on public.t1
Output: t1.rowid, t1.c1, t1.c2, t1.c3, t1.tableoid, (Expression Flatten Optimized)
(8 rows)
-- 清理数据
gaussdb=# DROP TABLE t1;
DROP TABLE
gaussdb=# DROP TABLE t1_p;
DROP TABLE ROWID系统列max/min优化
对于普通表场景,由于ROWID到rowno的映射严格保序,min/max聚集函数条件为ROWID单列时,可改写为rowno系统列的表达式进行优化。利用索引的有序性,将min/max对应的聚集操作优化为LIMIT和索引扫描,减少数据读取和计算开销。形如“SELECT max(rowid) FROM t1”的SQL语句遵循如下改写规则:
SELECT rowid FROM t1 ORDER BY rowno DESC LIMIT 1
ROWID系统列max/min优化遵从如下规格约束:
- 仅支持普通表场景,不支持分区表等。
- 当关闭GUC参数enable_indexscan时或者有其他不能进行rowno列索引扫描的场景时,该优化默认不生效。enable_indexscan参数详情可见《参考》中“数据库运行参数说明 > GUC参数说明 > 查询规划 > 优化器方法配置”章节内容。
示例:
由于不同场景下tableoid字段值不同,以下示例仅作参考,执行时以实际输出结果为准。
以GUC参数explain_perf_mode设置为normal为例。
-- 数据准备
gaussdb=# CREATE TABLE t1 (a INT, b INT) WITH (STORAGE_TYPE=Astore, hasrowid=ON);
CREATE TABLE
gaussdb=# INSERT INTO t1 VALUES ( generate_series(1,1000), generate_series(3001,4000));
INSERT 0 1000
gaussdb=# INSERT INTO t1 VALUES ( generate_series(1001,2000), generate_series(3001,4000));
INSERT 0 1000
gaussdb=# CREATE INDEX idx_t1_b ON t1(b);
CREATE INDEX
gaussdb=# ANALYZE t1;
ANALYZE
-- max/min优化场景
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ max(rowid) FROM t1;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
------------------------------------------------------------------------------------------
[Parameterized]
Result
Output: $0, (Expression Flatten Optimized)
InitPlan 1 (returns $0)
-> Limit
Output: public.t1.rowid, public.t1.rowno
-> Index Scan Backward using t1_rowno_idx ON public.t1
Output: public.t1.rowid, public.t1.rowno, (Expression Flatten Optimized)
(8 rows)
gaussdb=# SELECT /*+ indexscan(t1) */ rowid_sequence(max(rowid)) FROM t1; -- 2000
rowid_sequence
----------------
2000
(1 row)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ min(rowid) FROM t1;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
------------------------------------------------------------------------------------------
[Parameterized]
Result
Output: $0, (Expression Flatten Optimized)
InitPlan 1 (returns $0)
-> Limit
Output: public.t1.rowid, public.t1.rowno
-> Index Scan using t1_rowno_idx ON public.t1
Output: public.t1.rowid, public.t1.rowno, (Expression Flatten Optimized)
(8 rows)
gaussdb=# SELECT /*+ indexscan(t1) */ rowid_sequence(min(rowid)) FROM t1; -- 1
rowid_sequence
----------------
1
(1 row)
-- 多个max/min使用场景,支持优化
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid_sequence(max(rowid)),max(b) FROM t1;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
------------------------------------------------------------------------------------------
[Parameterized]
Result
Output: rowid_sequence($0), $1, (Expression Flatten Optimized)
InitPlan 1 (returns $0)
-> Limit
Output: public.t1.rowid, public.t1.rowno
-> Index Scan Backward using t1_rowno_idx ON public.t1
Output: public.t1.rowid, public.t1.rowno, (Expression Flatten Optimized)
InitPlan 2 (returns $1)
-> Limit
Output: public.t1.b
-> Index Only Scan Backward using idx_t1_b ON public.t1
Output: public.t1.b
Index Cond: (public.t1.b IS NOT NULL), (Expression Flatten Optimized)
(14 rows)
gaussdb=# SELECT /*+ indexscan(t1) */ rowid_sequence(max(rowid)),max(b) FROM t1; --2000, 4000
rowid_sequence | max
----------------+------
2000 | 4000
(1 row)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid_sequence(max(rowid)),max(rowno) FROM t1;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
------------------------------------------------------------------------------------------
[Parameterized]
Result
Output: rowid_sequence($0), $1, (Expression Flatten Optimized)
InitPlan 1 (returns $0)
-> Limit
Output: public.t1.rowid, public.t1.rowno
-> Index Scan Backward using t1_rowno_idx ON public.t1
Output: public.t1.rowid, public.t1.rowno, (Expression Flatten Optimized)
InitPlan 2 (returns $1)
-> Limit
Output: public.t1.rowno
-> Index Only Scan Backward using t1_rowno_idx ON public.t1
Output: public.t1.rowno
(13 rows)
gaussdb=# SELECT /*+ indexscan(t1) */ rowid_sequence(max(rowid)),max(rowno) FROM t1; --2000, 2000
rowid_sequence | max
----------------+------
2000 | 2000
(1 row)
-- 关闭enable_indexscan参数场景,不支持max/min优化
gaussdb=# SET enable_indexscan = off;
SET
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ max(rowid) FROM t1;
WARNING: unused hint: IndexScan(t1)
QUERY PLAN
-------------------------------------------------------
[Parameterized]
Aggregate
Output: max(rowid), (Expression Flatten Optimized)
-> Seq Scan ON public.t1
Output: rowid, (Expression Flatten Optimized)
(5 rows)
gaussdb=# SET enable_indexscan = ON;
SET
-- complex SQL,不支持max/min优化
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid_sequence(max(rowid)) FROM (SELECT rowid,a FROM t1 WHERE a = 1);
EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t1) */ rowid_sequence(max(rowid)) FROM t1 WHERE rownum < 100;WARNING: unused hint: IndexScan(t1)
QUERY PLAN
-------------------------------------------------------------------------
[Parameterized]
Aggregate
Output: rowid_sequence(max(t1.rowid)), (Expression Flatten Optimized)
-> Seq Scan ON public.t1
Output: t1.rowid, (Expression Flatten Optimized)
Filter: (t1.a = 1), (Expression Flatten Optimized)
(6 rows)
-- 分区表场景,不支持max/min优化
gaussdb=# -- PARTITION TABLE
gaussdb=# CREATE TABLE t2
gaussdb-# ( a INT,
gaussdb(# b INT,
gaussdb(# c INT)
gaussdb-# WITH (STORAGE_TYPE=Astore, hasrowid=ON)
gaussdb-# PARTITION BY RANGE (a)
gaussdb-# (
gaussdb(# PARTITION P1 VALUES LESS THAN(100),
gaussdb(# PARTITION P2 VALUES LESS THAN(200),
gaussdb(# PARTITION P3 VALUES LESS THAN(300),
gaussdb(# PARTITION P8 VALUES LESS THAN(MAXVALUE)
gaussdb(# ) ;
CREATE TABLE
gaussdb=# INSERT INTO t2 VALUES ( generate_series(1,1000), generate_series(1001,2000));
INSERT 0 1000
gaussdb=# ANALYZE t2;
ANALYZE
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t2) */ max(rowid) FROM t2;
WARNING: unused hint: IndexScan(t2)
QUERY PLAN
-------------------------------------------------------------
[Parameterized]
Aggregate
Output: max(rowid), (Expression Flatten Optimized)
-> Partition Iterator
Output: rowid
Iterations: 4
-> Partitioned Seq Scan ON public.t2
Output: rowid, (Expression Flatten Optimized)
Selected Partitions: 1..4
(9 rows)
gaussdb=# EXPLAIN (VERBOSE, COSTS FALSE) SELECT /*+ indexscan(t2) */ max(rowid) FROM t2 WHERE a = 1;
WARNING: unused hint: IndexScan(t2)
QUERY PLAN
------------------------------------------------------------
[Parameterized]
Aggregate
Output: max(rowid), (Expression Flatten Optimized)
-> Partitioned Seq Scan ON public.t2
Output: rowid, (Expression Flatten Optimized)
Filter: (t2.a = 1), (Expression Flatten Optimized)
Selected Partitions: 1
(7 rows)
-- 清理数据
gaussdb=# DROP TABLE t2;
DROP TABLE
gaussdb=# DROP TABLE t1;
DROP TABLE ROWID系统列分区剪枝
分区剪枝需要在ROWID系统列谓词条件被改写为包含tableoid等值条件的场景下触发,从而将查询裁剪至唯一的单个分区。该机制同时支持常量和参数两种条件:当为ROWID常量条件时触发静态剪枝,当为ROWID参数条件时触发动态剪枝。更多分区剪枝详情可见分区剪枝章节内容。
示例:
由于不同场景下tableoid字段值不同,以下示例仅作参考,执行时以实际输出结果为准。
以GUC参数explain_perf_mode设置为normal为例。
-- 数据准备
gaussdb=# SET auto_explain_level=notice;
SET
gaussdb=# CREATE TABLE t_part (c1 int, c2 int) WITH (storage_type=astore, hasrowid=on) partition by hash(c1) partitions 4;
CREATE TABLE
gaussdb=# INSERT INTO t_part SELECT generate_series(1,3), generate_series(1,4);
INSERT 0 12
gaussdb=# ANALYZE t_part;
ANALYZE
-- cplan计划
gaussdb=# SET plan_cache_mode = force_custom_plan;
SET
-- 常量条件静态剪枝至p2分区
gaussdb=# EXPLAIN (costs off, verbose on) SELECT * FROM t_part WHERE rowid = 'AAAAAAAAAP//AAAETGAAAAAAAAAAM';
QUERY PLAN
---------------------------------------------------------------------------
[Parameterized]
Partitioned Index Scan using t_part_rowno_idx on public.t_part
Output: c1, c2, (Expression Flatten Optimized)
Index Cond: (t_part.rowno = 12::bigint), (Expression Flatten Optimized)
Filter: (t_part.tableoid = 17606::oid), (Expression Flatten Optimized)
Selected Partitions: 2
(6 rows)
-- gplan计划
gaussdb=# SET plan_cache_mode = force_generic_plan;
SET
-- 参数条件动态剪枝至p2分区
gaussdb=# EXPLAIN (costs off, verbose on) SELECT * FROM t_part WHERE rowid = 'AAAAAAAAAP//AAAETGAAAAAAAAAAM';
QUERY PLAN
-----------------------------------------------------------------------------------------
[Parameterized]
Partition Iterator
Output: c1, c2
Iterations: PART
-> Partitioned Index Scan using t_part_rowno_idx on public.t_part
Output: c1, c2, (Expression Flatten Optimized)
Index Cond: (t_part.rowno = rowid_sequence($1)), (Expression Flatten Optimized)
Filter: (t_part.tableoid = rowid_tableoid($1)), (Expression Flatten Optimized)
Selected Partitions: 2 (pbe-pruning)
(9 rows)
-- 清理数据
gaussdb=# DROP TABLE t_part;
DROP TABLE
gaussdb=# RESET plan_cache_mode;
RESET
gaussdb=# RESET auto_explain_level;
RESET