PostgreSQL的set_base_rel_pathlists函数及其子函数分析
这篇文章主要讲解了“PostgreSQL的set_base_rel_pathlists函数及其子函数分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL的set_base_rel_pathlists函数及其子函数分析”吧!
set_base_rel_pathlists函数的目的是为每一个base rel找出所有可用的访问路径(包括顺序扫描和所有可用的索引),每一个可用的路径都会添加到pathlist链表中。这一小节主要介绍常规(区别于并行)顺序扫描部分。
make_one_rel源代码:
RelOptInfo *
make_one_rel(PlannerInfo *root, List *joinlist)
{
//...
set_base_rel_sizes(root);//估算Relation的Size并且设置consider_parallel标记
set_base_rel_pathlists(root);//生成Relation的扫描(访问)路径
rel = make_rel_from_joinlist(root, joinlist);
Assert(bms_equal(rel->relids, root->all_baserels));
//返回最上层的RelOptInfo
return rel;
}
一、数据结构
RelOptInfo
typedef struct RelOptInfo
{
NodeTag type;//节点标识
RelOptKind reloptkind;//RelOpt类型
Relids relids;
double rows;
bool consider_startup;
bool consider_param_startup;
bool consider_parallel;
struct PathTarget *reltarget;
List *pathlist;
List *ppilist;
List *partial_pathlist;
struct Path *cheapest_startup_path;//代价最低的启动路径
struct Path *cheapest_total_path;//代价最低的整体路径
struct Path *cheapest_unique_path;//代价最低的获取唯一值的路径
List *cheapest_parameterized_paths;//代价最低的参数化路径链表
Relids direct_lateral_relids;
Relids lateral_relids;
//reloptkind=RELOPT_BASEREL时使用的数据结构
Index relid;
Oid reltablespace;
RTEKind rtekind;
AttrNumber min_attr;
AttrNumber max_attr;
Relids *attr_needed;
int32 *attr_widths;
List *lateral_vars;
Relids lateral_referencers;
List *indexlist;
List *statlist;
BlockNumber pages;
double tuples;
double allvisfrac;
PlannerInfo *subroot;
List *subplan_params;
int rel_parallel_workers;
//FDW相关信息
Oid serverid;
Oid userid;
bool useridiscurrent;
struct FdwRoutine *fdwroutine;
void *fdw_private;
//已知的,可保证唯一元组返回的Relids链表
List *unique_for_rels;
List *non_unique_for_rels;
List *baserestrictinfo;
QualCost baserestrictcost;
Index baserestrict_min_security;
List *joininfo;
bool has_eclass_joins;
//是否尝试partitionwise连接,这是PG 11的一个新特性.
bool consider_partitionwise_join;
Relids top_parent_relids;
//分区表使用
PartitionScheme part_scheme;
int nparts;
struct PartitionBoundInfoData *boundinfo;
List *partition_qual;
struct RelOptInfo **part_rels;
List **partexprs;
List **nullable_partexprs;
List *partitioned_child_rels;
} RelOptInfo;
ParamPathInfo
typedef struct ParamPathInfo
{
NodeTag type;//节点类型
Relids ppi_req_outer;
double ppi_rows;
List *ppi_clauses;
} ParamPathInfo;
Cost相关
注意:实际使用的参数值通过系统配置文件定义,而不是这里的常量定义!
typedef struct QualCost
{
Cost startup;
Cost per_tuple;
} QualCost;
typedef double Cost;
#define DEFAULT_SEQ_PAGE_COST 1.0 //顺序扫描page的成本
#define DEFAULT_RANDOM_PAGE_COST 4.0 //随机扫描page的成本
#define DEFAULT_CPU_TUPLE_COST 0.01 //处理一个元组的CPU成本
#define DEFAULT_CPU_INDEX_TUPLE_COST 0.005 //处理一个索引元组的CPU成本
#define DEFAULT_CPU_OPERATOR_COST 0.0025 //执行一次操作或函数的CPU成本
#define DEFAULT_PARALLEL_TUPLE_COST 0.1 //并行执行,从一个worker传输一个元组到另一个worker的成本
#define DEFAULT_PARALLEL_SETUP_COST 1000.0 //构建并行执行环境的成本
#define DEFAULT_EFFECTIVE_CACHE_SIZE 524288
double seq_page_cost = DEFAULT_SEQ_PAGE_COST;
double random_page_cost = DEFAULT_RANDOM_PAGE_COST;
double cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
double cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
double cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
double parallel_tuple_cost = DEFAULT_PARALLEL_TUPLE_COST;
double parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST;
int effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
Cost disable_cost = 1.0e10;//1后面10个0,通过设置一个巨大的成本,让优化器自动放弃此路径
int max_parallel_workers_per_gather = 2;//每次gather使用的worker数
二、源码解读
set_base_rel_pathlists函数遍历RelOptInfo数组,为每一个Rel构造访问路径.
//--------------------------------------------------------
static void
set_base_rel_pathlists(PlannerInfo *root)
{
Index rti;
for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍历RelOptInfo数组
{
RelOptInfo *rel = root->simple_rel_array[rti];
if (rel == NULL)
continue;
Assert(rel->relid == rti);
if (rel->reloptkind != RELOPT_BASEREL)
continue;
set_rel_pathlist(root, rel, rti, root->simple_rte_array[rti]);
}
}
//-------------------------------------------------------- set_rel_pathlist
static void
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
if (IS_DUMMY_REL(rel))
{
}
else if (rte->inh)//inherit
{
set_append_rel_pathlist(root, rel, rti, rte);
}
else//常规
{
switch (rel->rtekind)
{
case RTE_RELATION://数据表
if (rte->relkind == RELKIND_FOREIGN_TABLE)//FDW
{
set_foreign_pathlist(root, rel, rte);
}
else if (rte->tablesample != NULL)//采样表
{
set_tablesample_rel_pathlist(root, rel, rte);
}
else//常规数据表
{
set_plain_rel_pathlist(root, rel, rte);
}
break;
case RTE_SUBQUERY://子查询
break;
case RTE_FUNCTION:
set_function_pathlist(root, rel, rte);
break;
case RTE_TABLEFUNC:
set_tablefunc_pathlist(root, rel, rte);
break;
case RTE_VALUES:
set_values_pathlist(root, rel, rte);
break;
case RTE_CTE:
break;
case RTE_NAMEDTUPLESTORE:
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
}
}
if (rel->reloptkind == RELOPT_BASEREL &&
bms_membership(root->all_baserels) != BMS_SINGLETON)
generate_gather_paths(root, rel, false);
if (set_rel_pathlist_hook)//钩子函数
(*set_rel_pathlist_hook) (root, rel, rti, rte);
set_cheapest(rel);//找出代价最低的访问路径
#ifdef OPTIMIZER_DEBUG
debug_print_rel(root, rel);
#endif
}
//-------------------------------------------------------- set_plain_rel_pathlist
static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
Relids required_outer;
required_outer = rel->lateral_relids;//需依赖的上层Relids
add_path(rel, create_seqscan_path(root, rel, required_outer, 0));
if (rel->consider_parallel && required_outer == NULL)
create_plain_partial_paths(root, rel);
create_index_paths(root, rel);
create_tidscan_paths(root, rel);
}
//-------------------------------------------------------- create_seqscan_path
Path *
create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer, int parallel_workers)
{
Path *pathnode = makeNode(Path);//顺序扫描路径
pathnode->pathtype = T_SeqScan;//顺序扫描
pathnode->parent = rel;//RelOptInfo
pathnode->pathtarget = rel->reltarget;//投影列
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);//获取参数化路径信息ParamPathInfo
pathnode->parallel_aware = parallel_workers > 0 ? true : false;//并行
pathnode->parallel_safe = rel->consider_parallel;//
pathnode->parallel_workers = parallel_workers;//并行数
pathnode->pathkeys = NIL;
cost_seqscan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
//-------------------------------------------- get_baserel_parampathinfo
ParamPathInfo *
get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel,
Relids required_outer)
{
ParamPathInfo *ppi;//ppi变量
Relids joinrelids;//参与连接的relids
List *pclauses;//条件链表
double rows;//行数
ListCell *lc;//临时变量
if (bms_is_empty(required_outer))
return NULL;
Assert(!bms_overlap(baserel->relids, required_outer));
if ((ppi = find_param_path_info(baserel, required_outer)))//已有缓存?
return ppi;//直接返回
joinrelids = bms_union(baserel->relids, required_outer);//合并relids
pclauses = NIL;
foreach(lc, baserel->joininfo)//遍历连接条件
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);//连接条件
if (join_clause_is_movable_into(rinfo,
baserel->relids,
joinrelids))
pclauses = lappend(pclauses, rinfo);//如可以移动,添加到链表中
}
pclauses = list_concat(pclauses,
generate_join_implied_equalities(root,
joinrelids,
required_outer,
baserel));
rows = get_parameterized_baserel_size(root, baserel, pclauses);//获取估算行数
ppi = makeNode(ParamPathInfo);//构造PPI返回节点
ppi->ppi_req_outer = required_outer;
ppi->ppi_rows = rows;
ppi->ppi_clauses = pclauses;
baserel->ppilist = lappend(baserel->ppilist, ppi);
return ppi;
}
//--------------------------------- get_parameterized_baserel_size
double
get_parameterized_baserel_size(PlannerInfo *root, RelOptInfo *rel,
List *param_clauses)
{
List *allclauses;
double nrows;
allclauses = list_concat(list_copy(param_clauses),
rel->baserestrictinfo);//合并条件
nrows = rel->tuples *
clauselist_selectivity(root,
allclauses,
rel->relid,
JOIN_INNER,
NULL);//获取行数
nrows = clamp_row_est(nrows);
if (nrows > rel->rows)
nrows = rel->rows;
return nrows;//返回
}
//-------------------------------------------- cost_seqscan
void
cost_seqscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel, ParamPathInfo *param_info)
{
Cost startup_cost = 0;//启动成本
Cost cpu_run_cost;//CPU成本
Cost disk_run_cost;//IO成本
double spc_seq_page_cost;//
QualCost qpqual_cost;//表达式成本
Cost cpu_per_tuple;//每个元组的CPU成本
Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_RELATION);
if (param_info)//存在PPI
path->rows = param_info->ppi_rows;//行数
else
path->rows = baserel->rows;//直接取基础关系行数
if (!enable_seqscan)
startup_cost += disable_cost;//不允许顺序扫描,disable_cost=1.0e10,即1后面10个0,这样的路径无需考虑
get_tablespace_page_costs(baserel->reltablespace,
NULL,
&spc_seq_page_cost);//获取顺序访问表空间page的成本
disk_run_cost = spc_seq_page_cost * baserel->pages;//IO成本
get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);//CPU成本
startup_cost += qpqual_cost.startup;//启动成本
cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;//处理每个元组的成本
cpu_run_cost = cpu_per_tuple * baserel->tuples;//CPU执行过程中的成本
startup_cost += path->pathtarget->cost.startup;//加上获取最终投影列的成本
cpu_run_cost += path->pathtarget->cost.per_tuple * path->rows;//加上获取最终投影列的成本
if (path->parallel_workers > 0)//并行执行
{
double parallel_divisor = get_parallel_divisor(path);//拆分多少份
cpu_run_cost /= parallel_divisor;//每一份的成本
path->rows = clamp_row_est(path->rows / parallel_divisor);//每一份的行数
}
path->startup_cost = startup_cost;//赋值
path->total_cost = startup_cost + cpu_run_cost + disk_run_cost;//总成本=启动 + 执行期的CPU和IO成本
}
//-------------------------------- get_tablespace_page_costs
void
get_tablespace_page_costs(Oid spcid,//表空间Oid
double *spc_random_page_cost,
double *spc_seq_page_cost)
{
TableSpaceCacheEntry *spc = get_tablespace(spcid);
Assert(spc != NULL);
if (spc_random_page_cost)//随机读取
{
if (!spc->opts || spc->opts->random_page_cost < 0)
*spc_random_page_cost = random_page_cost;
else
*spc_random_page_cost = spc->opts->random_page_cost;
}
if (spc_seq_page_cost)//顺序读取
{
if (!spc->opts || spc->opts->seq_page_cost < 0)
*spc_seq_page_cost = seq_page_cost;
else
*spc_seq_page_cost = spc->opts->seq_page_cost;
}
}
//-------------------------------- get_restriction_qual_cost
static void
get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info,
QualCost *qpqual_cost)
{
if (param_info)//参数化信息
{
cost_qual_eval(qpqual_cost, param_info->ppi_clauses, root);//评估成本
qpqual_cost->startup += baserel->baserestrictcost.startup;
qpqual_cost->per_tuple += baserel->baserestrictcost.per_tuple;
}
else
*qpqual_cost = baserel->baserestrictcost;//没有参数化信息,直接返回
}
//------------------- cost_qual_eval
void
cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
{
cost_qual_eval_context context;
ListCell *l;
context.root = root;
context.total.startup = 0;
context.total.per_tuple = 0;
foreach(l, quals)//遍历链表
{
Node *qual = (Node *) lfirst(l);
cost_qual_eval_walker(qual, &context);//遍历表达式
}
*cost = context.total;//返回总成本
}
//------------ cost_qual_eval_walker
static bool
cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
{
if (node == NULL)
return false;
if (IsA(node, RestrictInfo))//约束条件
{
RestrictInfo *rinfo = (RestrictInfo *) node;
if (rinfo->eval_cost.startup < 0)//未计算成本,初始值为-1
{
cost_qual_eval_context locContext;
locContext.root = context->root;
locContext.total.startup = 0;
locContext.total.per_tuple = 0;
if (rinfo->orclause)
cost_qual_eval_walker((Node *) rinfo->orclause, &locContext);//递归OR条件
else
cost_qual_eval_walker((Node *) rinfo->clause, &locContext);//递归
if (rinfo->pseudoconstant)//pseudoconstant标志为T
{
locContext.total.startup += locContext.total.per_tuple;
locContext.total.per_tuple = 0;
}
rinfo->eval_cost = locContext.total;
}
context->total.startup += rinfo->eval_cost.startup;
context->total.per_tuple += rinfo->eval_cost.per_tuple;
return false;
}
if (IsA(node, FuncExpr))//函数表达式
{
context->total.per_tuple +=
get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;//调用get_func_cost函数
}
else if (IsA(node, OpExpr) ||
IsA(node, DistinctExpr) ||
IsA(node, NullIfExpr))//操作符/Distinct/NullIf判断,调用get_func_cost
{
set_opfuncid((OpExpr *) node);
context->total.per_tuple +=
get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
}
else if (IsA(node, ScalarArrayOpExpr))//数组
{
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
Node *arraynode = (Node *) lsecond(saop->args);
set_sa_opfuncid(saop);
context->total.per_tuple += get_func_cost(saop->opfuncid) *
cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
}
else if (IsA(node, Aggref) ||
IsA(node, WindowFunc))//聚合函数或者窗口函数
{
return false;
}
else if (IsA(node, CoerceViaIO))//CoerceViaIO
{
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
Oid iofunc;
Oid typioparam;
bool typisvarlena;
getTypeInputInfo(iocoerce->resulttype,
&iofunc, &typioparam);
context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
getTypeOutputInfo(exprType((Node *) iocoerce->arg),
&iofunc, &typisvarlena);
context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
}
else if (IsA(node, ArrayCoerceExpr))//ArrayCoerceExpr
{
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
QualCost perelemcost;
cost_qual_eval_node(&perelemcost, (Node *) acoerce->elemexpr,
context->root);
context->total.startup += perelemcost.startup;
if (perelemcost.per_tuple > 0)
context->total.per_tuple += perelemcost.per_tuple *
estimate_array_length((Node *) acoerce->arg);
}
else if (IsA(node, RowCompareExpr))//RowCompareExpr
{
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
ListCell *lc;
foreach(lc, rcexpr->opnos)
{
Oid opid = lfirst_oid(lc);
context->total.per_tuple += get_func_cost(get_opcode(opid)) *
cpu_operator_cost;
}
}
else if (IsA(node, MinMaxExpr) ||
IsA(node, SQLValueFunction) ||
IsA(node, XmlExpr) ||
IsA(node, CoerceToDomain) ||
IsA(node, NextValueExpr))//最小最大值/SQLValueFunction/XML表达式/CoerceToDomain/NextValueExpr
{
context->total.per_tuple += cpu_operator_cost;
}
else if (IsA(node, CurrentOfExpr))//CurrentOfExpr
{
context->total.startup += disable_cost;//不考虑顺序扫描,使用TID扫描
}
else if (IsA(node, SubLink))
{
elog(ERROR, "cannot handle unplanned sub-select");//子链接,报错
}
else if (IsA(node, SubPlan))//子计划
{
SubPlan *subplan = (SubPlan *) node;
context->total.startup += subplan->startup_cost;//直接相加
context->total.per_tuple += subplan->per_call_cost;
return false;
}
else if (IsA(node, AlternativeSubPlan))//AlternativeSubPlan
{
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
return cost_qual_eval_walker((Node *) linitial(asplan->subplans),
context);
}
else if (IsA(node, PlaceHolderVar))//PHV,成本为0
{
return false;
}
return expression_tree_walker(node, cost_qual_eval_walker,
(void *) context);//递归到子节点中
}
//------- get_func_cost
float4
get_func_cost(Oid funcid)
{
HeapTuple tp;
float4 result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));//获取函数Oid
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", funcid);
//查询数据字典:select proname,procost from pg_proc where procost > 1;
result = ((Form_pg_proc) GETSTRUCT(tp))->procost;//直接获取函数的procost
ReleaseSysCache(tp);
return result;
}
三、跟踪分析
启动gdb:
(gdb) b set_base_rel_pathlists
Breakpoint 1 at 0x73bfb5: file allpaths.c, line 296.
(gdb) c
Continuing.
Breakpoint 1, set_base_rel_pathlists (root=0x2fd9418) at allpaths.c:296
296 for (rti = 1; rti < root->simple_rel_array_size; rti++)
进入set_plain_rel_pathlist:
(gdb)
452 set_plain_rel_pathlist(root, rel, rte);
(gdb) step
set_plain_rel_pathlist (root=0x2fd9418, rel=0x2f98278, rte=0x2eaa5d8) at allpaths.c:704
704 required_outer = rel->lateral_relids;
进入create_seqscan_path:
(gdb) step
create_seqscan_path (root=0x2fd9418, rel=0x2f98278, required_outer=0x0, parallel_workers=0) at pathnode.c:957
957 Path *pathnode = makeNode(Path);
(gdb)
969 cost_seqscan(pathnode, root, rel, pathnode->param_info);
(gdb) p *pathnode
$2 = {type = T_Path, pathtype = T_SeqScan, parent = 0x2f98278, pathtarget = 0x2f98488, param_info = 0x0,
parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 0, startup_cost = 0, total_cost = 0,
pathkeys = 0x0}
进入cost_seqscan:
...
(gdb)
230 path->rows = baserel->rows;
#rows的获得上一节已作介绍
(gdb) p baserel->rows
$4 = 10926
...
#表空间顺序扫描的成本
(gdb) p spc_seq_page_cost
$5 = 1
#IO成本
(gdb) p disk_run_cost
$6 = 726
进入get_restriction_qual_cost:
(gdb) step
get_restriction_qual_cost (root=0x2fd9418, baserel=0x2f98278, param_info=0x0, qpqual_cost=0x7ffe12ca4620) at costsize.c:3999
3999 if (param_info)
#没有参数化信息,直接使用baserel->baserestrictcost
(gdb) n
4008 *qpqual_cost = baserel->baserestrictcost;
(gdb) p baserel->baserestrictcost
$7 = {startup = 0, per_tuple = 0.0050000000000000001}
回到cost_seqscan
(gdb)
cost_seqscan (path=0x2f98990, root=0x2fd9418, baserel=0x2f98278, param_info=0x0) at costsize.c:248
248 startup_cost += qpqual_cost.startup;
...
执行cost_seqscan,最终的path:
(gdb) p *path
$8 = {type = T_Path, pathtype = T_SeqScan, parent = 0x2f98278, pathtarget = 0x2f98488, param_info = 0x0,
parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 10926, startup_cost = 0, total_cost = 2226,
pathkeys = 0x0}
(gdb) p cpu_run_cost
$9 = 1500
(gdb) p disk_run_cost
$10 = 726
回到上层函数:
(gdb) n
create_seqscan_path (root=0x2fd9418, rel=0x2f98278, required_outer=0x0, parallel_workers=0) at pathnode.c:971
971 return pathnode;
(gdb)
972 }
(gdb)
set_plain_rel_pathlist (root=0x2fd9418, rel=0x2f98278, rte=0x2eaa5d8) at allpaths.c:710
710 if (rel->consider_parallel && required_outer == NULL)
继续执行构建索引扫描路径/TID扫描路径函数:
714 create_index_paths(root, rel);
(gdb)
717 create_tidscan_paths(root, rel);
(gdb) n
718 }
索引扫描路径的结果,rows = 10926, startup_cost = 324.40899999999999,total_cost = 1214.299
(gdb) p *rel->pathlist
$14 = {type = T_List, length = 1, head = 0x2fe8d10, tail = 0x2fe8d10}
(gdb) p *(Path *)rel->pathlist->head->data.ptr_value
$15 = {type = T_BitmapHeapPath, pathtype = T_BitmapHeapScan, parent = 0x2f98278, pathtarget = 0x2f98488, param_info = 0x0,
parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 10926, startup_cost = 324.40899999999999,
total_cost = 1214.299, pathkeys = 0x0}
结束调用
(gdb)
set_base_rel_pathlists (root=0x2fd9418) at allpaths.c:296
296 for (rti = 1; rti < root->simple_rel_array_size; rti++)
(gdb)
312 }
(gdb)
make_one_rel (root=0x2fd9418, joinlist=0x2f985d8) at allpaths.c:185
185 rel = make_rel_from_joinlist(root, joinlist);
#DONE!
相应的SQL执行计划,cost=324.41..1214.30请参照索引扫描路径结果(这部分源码下一节分析):
testdb=# explain analyze verbose select t1.* from t_dwxx t1 where dwbh > '10000' and dwbh < '20000';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
---
Bitmap Heap Scan on public.t_dwxx t1 (cost=324.41..1214.30 rows=10926 width=23) (actual time=3.196..4.959 rows=11111 loops=
1)
Output: dwmc, dwbh, dwdz
Recheck Cond: (((t1.dwbh)::text > '10000'::text) AND ((t1.dwbh)::text < '20000'::text))
Heap Blocks: exact=85
-> Bitmap Index Scan on t_dwxx_pkey (cost=0.00..321.68 rows=10926 width=0) (actual time=3.159..3.159 rows=11111 loops=1)
Index Cond: (((t1.dwbh)::text > '10000'::text) AND ((t1.dwbh)::text < '20000'::text))
Planning Time: 0.315 ms
Execution Time: 5.673 ms
(8 rows)
感谢各位的阅读,以上就是“PostgreSQL的set_base_rel_pathlists函数及其子函数分析”的内容了,经过本文的学习后,相信大家对PostgreSQL的set_base_rel_pathlists函数及其子函数分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341