Postgresql源码(134)优化器针对volatile函数的排序优化分析

news2024/10/7 9:33:11

相关
《Postgresql源码(133)优化器动态规划生成连接路径的实例分析》

上一篇对路径的生成进行了分析,通过make_one_rel最终拿到了一个带着路径的RelOptInfo。本篇针对带volatile函数的排序场景继续分析subquery_planner的后续流程。

subquery_planner
	grouping_planner
		query_planner
			make_one_rel   <<< 上一篇
		// 后续流程         <<< 本篇

总结速查

一句话总结:带有volatile的投影列会被SORT算子忽略,达到先排序在投影计算volatile的效果。

  • grouping_planner→make_one_rel层层生成path,每个path都会带pathtarget(不一定是SQL中最后需要的target列表),一般都是层层继承上来的。
  • make_one_rel生成的最终path中,会忽略volatile函数列,交给外层grouping_planner函数处理,所以生成的path中的pathtarget都是看不到volatile函数列的。
  • 这里一个关键逻辑就path中的pathtargetmake_sort_input_target计算出来列表的是不是一样的
    • 如果是一样的就不加投影节点,等后面加sort时(create_ordered_paths)先加sort在加投影,计算顺序就是先排序,在拿排序阶段投影(计算random函数)
    • 如果不一样就直接加投影节点,后面sort会加到投影上面,计算顺序就是先投影(计算random函数),再排序。
  • path中的pathtarget会忽略volatile函数。
  • make_sort_input_target中的volatile函数正常也会被忽略掉(实例3),除非volatile函数就是排序列(实例4)。

最终效果是,投影列有volatile函数的SQL(函数非排序列),sort节点会忽略这类函数的执行,sort结束后,在投影节点使用sort的结果集来计算这类函数。

实例3:
在这里插入图片描述

1 实例:简单join

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);

drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);

drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);

drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);


explain 
SELECT STUDENT.sname, COURSE.cname, SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno;

                                  QUERY PLAN
------------------------------------------------------------------------------
 Hash Left Join  (cost=69.50..110.65 rows=2040 width=80)
   Hash Cond: (score.cno = course.cno)
   ->  Hash Right Join  (cost=34.75..70.53 rows=2040 width=46)
         Hash Cond: (score.sno = student.sno)
         ->  Seq Scan on score  (cost=0.00..30.40 rows=2040 width=12)
         ->  Hash  (cost=21.00..21.00 rows=1100 width=42)
               ->  Seq Scan on student  (cost=0.00..21.00 rows=1100 width=42)
   ->  Hash  (cost=21.00..21.00 rows=1100 width=42)
         ->  Seq Scan on course  (cost=0.00..21.00 rows=1100 width=42)

1.1 subquery_planner→grouping_planner

grouping_planner
	current_rel = query_planner(root, standard_qp_callback, &qp_extra);
  • current_rel:
    在这里插入图片描述
	final_target = create_pathtarget(root, root->processed_tlist);
  • 得到final_target
    • final_target->exprs->elements[0] : {varno = 1, varattno = 2, vartype = 1043} STUDENT.sname
    • final_target->exprs->elements[1] : {varno = 4, varattno = 2, vartype = 1043} COURSE.cname
    • final_target->exprs->elements[2] : {varno = 2, varattno = 3, vartype = 23} SCORE.degree
	if (parse->sortClause)
		make_sort_input_target
	if (activeWindows)
	 	...
	if (have_grouping)
		...
	if (parse->hasTargetSRFs)
		...
  • apply_scanjoin_target_to_paths创建投影节点
    在这里插入图片描述
	/* Apply scan/join target. */
	scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1
		&& equal(scanjoin_target->exprs, current_rel->reltarget->exprs);
	apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets,
								   scanjoin_targets_contain_srfs,
								   scanjoin_target_parallel_safe,
								   scanjoin_target_same_exprs);
  • 继续
	if (have_grouping)
		...
	if (activeWindows)
		...
	if (parse->distinctClause)
		...
	if (parse->sortClause)
		create_ordered_paths
  • 创建空的最顶层节点
	final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
  • 遍历current_rel中所有的path,用add_path加入到最顶层节点中。
  • 其中limit、rowclock的场景需要特殊处理下。
	foreach(lc, current_rel->pathlist)
		if (parse->rowMarks)
			create_lockrows_path
		if (limit_needed(parse))
			create_limit_path
		add_path(final_rel, path);

grouping_planner函数执行结束,最后拼接的final_rel在upper_rels里面记录:
在这里插入图片描述
pathlist最上层是投影节点:
在这里插入图片描述

1.2 standard_planner→subquery_planner

subquery_planner中后续处理流程:

计划生成步骤作用
root = subquery_planner优化器入口,返回PlannerInfo,里面记录了一个最终的RelOptInfo相当于一张逻辑表,每个ROI都记录了多个path,表示不同的计算路径
final_rel = fetch_upper_rel拿到最终的RelOptInfo
best_path = get_cheapest_fractional_path在RelOptInfo中选择一个最优的path
top_plan = create_plan→create_plan_recurse根据最优path生成计划

2 实例:【简单join】【排序非投影列】【投影列无函数】

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);

drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);

drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);

drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);


explain verbose
SELECT STUDENT.sname, COURSE.cname, SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;

                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Sort  (cost=3.44..3.46 rows=8 width=19)
   Output: student.sname, course.cname, score.degree, course.cno
   Sort Key: course.cno
   ->  Hash Left Join  (cost=2.14..3.32 rows=8 width=19)
         Output: student.sname, course.cname, score.degree, course.cno
         Inner Unique: true
         Hash Cond: (score.cno = course.cno)
         ->  Hash Right Join  (cost=1.09..2.21 rows=8 width=13)
               Output: student.sname, score.degree, score.cno
               Inner Unique: true
               Hash Cond: (score.sno = student.sno)
               ->  Seq Scan on public.score  (cost=0.00..1.08 rows=8 width=12)
                     Output: score.sno, score.cno, score.degree
               ->  Hash  (cost=1.04..1.04 rows=4 width=9)
                     Output: student.sname, student.sno
                     ->  Seq Scan on public.student  (cost=0.00..1.04 rows=4 width=9)
                           Output: student.sname, student.sno
         ->  Hash  (cost=1.02..1.02 rows=2 width=10)
               Output: course.cname, course.cno
               ->  Seq Scan on public.course  (cost=0.00..1.02 rows=2 width=10)
                     Output: course.cname, course.cno

2.1 grouping_planner

grouping_planner
	current_rel = query_planner(root, standard_qp_callback, &qp_extra);
	final_target = create_pathtarget(root, root->processed_tlist);
	if (parse->sortClause)
		sort_input_target = make_sort_input_target(root, final_target, &have_postponed_srfs);

make_sort_input_target函数的作用:

  • 排序列可能不在最终的投影列里面,需要特殊处理下。
  • 易变函数和成本很高的函数需要再投影列中识别出来,先排序,在计算。
    • 因为1:sort limit场景可以少算一些。
    • 因为2:易变函数每次算都可能不一样,先排序好了再算有利于结果集稳定,例如current_timestamp这种,期望是排序后给出的每一样的时间都是递增的,如果先排序在计算就能得到这种效果。

生成的final_target和sort_input_target相同,因为没看到srf函数、易变函数。

final_target同sort_input_targetVar指向列sortgrouprefs
final_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
final_target->exprs->elements[1]varno = 4, varattno = 2, vartype = 1043COURSE.cname0
final_target->exprs->elements[2]varno = 2, varattno = 3, vartype = 23SCORE.degree0
final_target->exprs->elements[3]varno = 4, varattno = 1, vartype = 23COURSE.cno1

grouping_planner继续执行,开始生成排序path:

	...
	if (parse->sortClause)
		current_rel = create_ordered_paths(root,
										   current_rel,
										   final_target,
										   final_target_parallel_safe,
										   have_postponed_srfs ? -1.0 :
										   limit_tuples);

grouping_planner→create_ordered_paths

create_ordered_paths
	// 创建一个排序节点
	ordered_rel = fetch_upper_rel(root, UPPERREL_ORDERED, NULL);

	// 拿到path入口,目前顶层是T_ProjectionPath,就一个节点
	foreach(lc, input_rel->pathlist)
		// 判断input_path->pathkeys是不是有序的?
		// 因为现在计划树是hashjoin,每一列都是无序的,所以input_path->pathkeys是空的,需要排序
		is_sorted = pathkeys_count_contained_in(root->sort_pathkeys, input_path->pathkeys, &presorted_keys);
		if (is_sorted)
			sorted_path = input_path;
		else
			sorted_path = (Path *) create_sort_path(root,
														ordered_rel,
														input_path,
														root->sort_pathkeys,
														limit_tuples);
		
  • 输入的path顶层节点是project本来没有带pathkeys信息,这里创建一个sort节点放在上面,加入pathkey信息。
  • 但生成的sortpath没看到排序列的信息?
  • 排序信息在基类path的pathkeys中。
sorted_path = 
{ path = 
  { type = T_SortPath, 
    pathtype = T_Sort, 
    parent = 0x2334030, 
    pathtarget = 0x2333ef0, 
    param_info = 0x0, 
    parallel_aware = false, parallel_safe = true, parallel_workers = 0, 
    rows = 8, 
    startup_cost = 3.4437500000000005, 
    total_cost = 3.4637500000000006, 
    pathkeys = 0x232e018}, 
  subpath = 0x2333a00}

T_PathKey每个pathkey(排序列)都对应了一个T_EquivalenceClass,T_EquivalenceClass中记录了排序的具体信息。

{ type = T_PathKey, 
  pk_eclass = 0x232bf88, 
  pk_opfamily = 1976, 
  pk_strategy = 1, 
  pk_nulls_first = false}

T_EquivalenceClass中的ec_members记录了排序列信息Var{varno = 4, varattno = 1}

{ type = T_EquivalenceClass, 
  ec_opfamilies = 0x232ddf8,    // List{ 1976 }
  ec_collation = 0, 
  ec_members = 0x232df48,  // List { EquivalenceMember }
                           // EquivalenceMember{
                           //   type = T_EquivalenceMember, 
                           //   em_expr = 0x232de68,  Var{varno = 4, varattno = 1}
                           //   em_relids = 0x232de48, 
                           //   em_is_const = false, 
                           //   em_is_child = false, 
                           //   em_datatype = 23, 
                           //   em_jdomain = 0x2329158, em_parent = 0x0}
  ec_sources = 0x0, 
  ec_derives = 0x0, 
  ec_relids = 0x232df28,
  ec_has_const = false, 
  ec_has_volatile = false, 
  ec_broken = false, 
  ec_sortref = 1, 
  ec_min_security = 4294967295, 
  ec_max_security = 0, 
  ec_merged = 0x0}

生成排序节点后的计划:

  • sort节点的target是四列,虽然sql只写了三列,但有一列是排序需要的,也会加到pathtarget中。
    在这里插入图片描述

3 实例:【简单join】【排序非投影列】【投影列中有volatile函数】

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);

drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);

drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);

drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);


explain verbose
SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;
                                         QUERY PLAN
--------------------------------------------------------------------------------------------
 Result  (cost=3.44..3.56 rows=8 width=21)
   Output: student.sname, random(), score.degree, course.cno
   ->  Sort  (cost=3.44..3.46 rows=8 width=13)
         Output: student.sname, score.degree, course.cno
         Sort Key: course.cno
         ->  Hash Left Join  (cost=2.14..3.32 rows=8 width=13)
               Output: student.sname, score.degree, course.cno
               Inner Unique: true
               Hash Cond: (score.cno = course.cno)
               ->  Hash Right Join  (cost=1.09..2.21 rows=8 width=13)
                     Output: student.sname, score.degree, score.cno
                     Inner Unique: true
                     Hash Cond: (score.sno = student.sno)
                     ->  Seq Scan on public.score  (cost=0.00..1.08 rows=8 width=12)
                           Output: score.sno, score.cno, score.degree
                     ->  Hash  (cost=1.04..1.04 rows=4 width=9)
                           Output: student.sname, student.sno
                           ->  Seq Scan on public.student  (cost=0.00..1.04 rows=4 width=9)
                                 Output: student.sname, student.sno
               ->  Hash  (cost=1.02..1.02 rows=2 width=4)
                     Output: course.cno
                     ->  Seq Scan on public.course  (cost=0.00..1.02 rows=2 width=4)
                           Output: course.cno

3.1 grouping_planner→make_one_rel生成的RelOptInfo→reltarget

make_one_rel前:

准备连接的RelOptInfo在simple_rel_array数组中,这里关注下三个RelOptInfo的reltarget:

(gdb) plist root->simple_rel_array[1]->reltarget->exprs
$67 = 2
$68 = {ptr_value = 0x3083218, int_value = 50868760, oid_value = 50868760, xid_value = 50868760}
$69 = {ptr_value = 0x30ab8b8, int_value = 51034296, oid_value = 51034296, xid_value = 51034296}
(gdb) p root->simple_rte_array[1]->relid
$70 = 16564
root→simple_rel_array[i]simple_rel_array[i]→reltarget->exprsrelid
1varno = 1, varattno = 2, vartype = 104316564 student.sname
1varno = 1, varattno = 1, vartype = 2316564 student.sno
2varno = 2, varattno = 3, vartype = 2316579 score.degree
2varno = 2, varattno = 1, vartype = 2316579 score.cno
2varno = 2, varattno = 2, vartype = 2316579 score.sno
4varno = 4, varattno = 1, vartype = 2316569 course.cno
SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;

make_one_rel生成后:

final_rel->reltarget->exprs
1varno = 1, varattno = 2, vartype = 1043投影第1列:STUDENT.sname
2varno = 2, varattno = 3, vartype = 23投影第3列:SCORE.degree
3varno = 4, varattno = 1, vartype = 23排序列:COURSE.cno

3.2 grouping_planner→make_sort_input_target规律v函数生成排序target

final_target = create_pathtarget(root, root->processed_tlist);拿到的final_target:

final_targetVar / FuncExpr指向列sortgrouprefs
final_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
final_target->exprs->elements[1]funcid = 1598, funcresulttype = 701random()0
final_target->exprs->elements[2]varno = 2, varattno = 3, vartype = 23SCORE.degree0
final_target->exprs->elements[3]varno = 4, varattno = 1, vartype = 23COURSE.cno1

make_sort_input_target拿到的sort_input_target,过滤掉了random列:

sort_input_targetVar / FuncExpr指向列sortgrouprefs
sort_input_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
sort_input_target->exprs->elements[1]varno = 2, varattno = 3, vartype = 23SCORE.degree0
sort_input_target->exprs->elements[2]varno = 4, varattno = 1, vartype = 23COURSE.cno1

实例2中,apply_scanjoin_target_to_paths会先挂投影节点,后面的create_ordered_paths在创建顶层的排序节点,为什么这里的投影节点在最上层?因为有volatile函数在,需要先排序,在到投影节点上计算random函数

3.3 grouping_planner→apply_scanjoin_target_to_paths

		final_target = create_pathtarget(root, root->processed_tlist);
		...
		sort_input_target = make_sort_input_target(...);
		...
		grouping_target = sort_input_target;
		...
		scanjoin_target = grouping_target;
		...
		scanjoin_targets = list_make1(scanjoin_target);
		...
		scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1
			&& equal(scanjoin_target->exprs, current_rel->reltarget->exprs);
		...
		// 1 确定没有SRF  list_length(scanjoin_targets) == 1
		// 2 这里make_one_rel出来的current_rel和上面make_sort_input_target计算出来的投影列一样,都过滤掉了v函数,剩下三列
		// scanjoin_target_same_exprs == true

		scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1
			&& equal(scanjoin_target->exprs, current_rel->reltarget->exprs);
		apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets,
									   scanjoin_targets_contain_srfs,
									   scanjoin_target_parallel_safe,

注意:

  1. scanjoin_target->exprs:表示最终结果需要的targetlist。
  2. current_rel->reltarget->exprs:表示当前生成path中带的targetlist。
  3. 生成path的路径需要和scanjoin_target一致,所以进入下面函数判断是否生成投影节点。
  4. 如果相同,scanjoin_target_same_exprs==true,则不生成投影节点。
  5. 如果不同,scanjoin_target_same_exprs==false,则调用create_projection_path传入scanjoin_target,生成投影节点。

在apply_scanjoin_target_to_paths中:

apply_scanjoin_target_to_paths
	...
	...
	foreach(lc, rel->pathlist)
	{
		Path	   *subpath = (Path *) lfirst(lc);

		if (tlist_same_exprs)
			// scanjoin_target->sortgrouprefs = [0, 0, 1] 表示第三列是排序列
			// 因为现在的scanjoin_target(同sort_input_target)中只有三列,投影列1、3和排序列,参考上面sort_input_target表格。
			subpath->pathtarget->sortgrouprefs = scanjoin_target->sortgrouprefs;
		else
		{
			Path	   *newpath;
			newpath = (Path *) create_projection_path(root, rel, subpath,
													  scanjoin_target);
			lfirst(lc) = newpath;
		}
	}

3.4 grouping_planner→create_ordered_paths

继续成成排序node:

grouping_planner
	...
	if (parse->sortClause)
				current_rel = create_ordered_paths(root,
										   current_rel,
										   final_target,
										   final_target_parallel_safe,
										   have_postponed_srfs ? -1.0 :
										   limit_tuples);
  • create_ordered_paths最重要的入参就是final_target,保存了全部的列信息和排序列的位置sortgrouprefs。
  • 注意前面生成path中的reltarget已经过滤了random列,但这里没有过滤,需要全量的信息。
final_targetVar / FuncExpr指向列sortgrouprefs
final_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
final_target->exprs->elements[1]funcid = 1598, funcresulttype = 701random()0
final_target->exprs->elements[2]varno = 2, varattno = 3, vartype = 23SCORE.degree0
final_target->exprs->elements[3]varno = 4, varattno = 1, vartype = 23COURSE.cno1
  1. 注意:这里create_sort_path为hashjoin节点上面加了一层sort节点,sort节点的pathtarget继承了hash节点的pathtarget,也就是三列(没有random函数列)。
  2. 注意:这里的target是上面表格中的final_target,也就是四列(带random函数)。
  3. 加了sort节点后,发现这里不相同,所以开始增加投影列apply_projection_to_path。
create_ordered_paths
	ordered_rel = fetch_upper_rel(root, UPPERREL_ORDERED, NULL);
	
	foreach(lc, input_rel->pathlist)
		is_sorted = pathkeys_count_contained_in
		if (is_sorted)
			sorted_path = input_path;
		else
			sorted_path = (Path *) create_sort_path(...)

		// 生成sorted_path
		// {type = T_SortPath, pathtype = T_Sort, pathtarget = 三列 }
		
		if (sorted_path->pathtarget != target)
			sorted_path = apply_projection_to_path(root, ordered_rel, sorted_path, target);
		
		// 生成投影列
		// {type = T_ProjectionPath, pathtype = T_Result, pathtarget = 四列 }

最终生成PATH:

SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;

最终效果:
在这里插入图片描述

4 实例:【简单join】【排序volatile函数】【投影列中有volatile函数】

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);

drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);

drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);

drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);


explain verbose
SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY random();

                                   QUERY PLAN
--------------------------------------------------------------------------------
 Sort  (cost=2.35..2.37 rows=8 width=17)
   Output: student.sname, (random()), score.degree
   Sort Key: (random())
   ->  Hash Right Join  (cost=1.09..2.23 rows=8 width=17)
         Output: student.sname, random(), score.degree
         Inner Unique: true
         Hash Cond: (score.sno = student.sno)
         ->  Seq Scan on public.score  (cost=0.00..1.08 rows=8 width=12)
               Output: score.sno, score.cno, score.degree
         ->  Hash  (cost=1.04..1.04 rows=4 width=9)
               Output: student.sname, student.sno
               ->  Seq Scan on public.student  (cost=0.00..1.04 rows=4 width=9)
                     Output: student.sname, student.sno

4.1 make_one_rel结果

第一步:拿到RelOptInfo
current_rel = query_planner(root, standard_qp_callback, &qp_extra);

current_rel->reltarget中忽略了random函数:

{ 
  type = T_PathTarget, 
  exprs = 
    {
    	Var{varno = 1, varattno = 2, vartype = 1043}, // STUDENT.sname
    	Var{varno = 2, varattno = 3, vartype = 23}    // SCORE.degree
    }, 
  sortgrouprefs = 0x0 }

4.2 拿到final_target

final_target = create_pathtarget(root, root->processed_tlist);

{
 	type = T_PathTarget, 
 	exprs = 
 	{
 		Var{varno = 1, varattno = 2, vartype = 1043},         // STUDENT.sname
 		FuncExpr {xpr = {type = T_FuncExpr}, funcid = 1598},  // random()
 		Var{varno = 2, varattno = 3, vartype = 23}            // SCORE.degree
 	}, 
 	sortgrouprefs = [0, 1, 0]
}

4.3 构造排序target:make_sort_input_target

sort_input_target = make_sort_input_target(root, final_target, &have_postponed_srfs);

{
	type = T_PathTarget,
	 exprs = 
	 {
 		Var{varno = 1, varattno = 2, vartype = 1043},         // STUDENT.sname
 		FuncExpr {xpr = {type = T_FuncExpr}, funcid = 1598},  // random()
 		Var{varno = 2, varattno = 3, vartype = 23}            // SCORE.degree
	 }, 
	 sortgrouprefs = [0, 1, 0]
}

4.4 apply_scanjoin_target_to_paths增加投影

apply_scanjoin_target_to_paths执行后,增加投影节点:

{ path = {type = T_ProjectionPath, pathtype = T_Result }

4.5 create_ordered_paths后增加排序节点在最顶层

{ path = {type = T_SortPath, pathtype = T_Sort }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1707254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode题练习与总结:有序链表转换二叉搜索树--109

一、题目描述 给定一个单链表的头节点 head &#xff0c;其中的元素 按升序排序 &#xff0c;将其转换为平衡二叉搜索树。 示例 1: 输入: head [-10,-3,0,5,9] 输出: [0,-3,9,-10,null,5] 解释: 一个可能的答案是[0&#xff0c;-3,9&#xff0c;-10,null,5]&#xff0c;它表…

eBPF可观测之网络流量控制和管理traffic control浅尝

目录 工程背景 环境准备 安装工具​​​ 安装依赖包 安装C依赖库 操作步骤 目录结构 代码展示 效果展示 拓展提升 工程背景 首先发表一个"暴论" eBPF在可观测方面的应用&#xff0c;就是各种google。 不需要学习内核&#xff0c;只要掌握ebpf开发套路。…

3D应用开发工具HOOPS如何优化复杂3D大模型的实时渲染和交互?

在现代工程设计、仿真和可视化领域&#xff0c;处理和交互大型三维模型&#xff08;3D Models&#xff09;的需求日益增加。随着模型复杂度和数据量的增长&#xff0c;如何有效地管理和实时操作这些模型成为了一个关键挑战。HOOPS技术以其卓越的速度和效率&#xff0c;成为了应…

java项目——图书管理系统

文章目录 前言图书管理系统整体框架&#xff1a;book包user包Main包&#xff1a;iooperation包总结&#xff1a; 前言 针对这些天所学的javaSE的知识&#xff0c;用一个小项目来实践一下。 图书管理系统 整体框架&#xff1a; 采取面向对象的思想实现此项目&#xff0c;首先…

SALOME源码分析:MDF框架

SALOME是由EDF、CEA、Open CASCADE等联合开发的开源CAE集成平台。 作为一款开源CAE软件集成平台&#xff0c;SALOME以其现代化的架构设计、良好的扩展性&#xff0c;提供了几何建模、网格生成、数据同化、求解器调用、后处理可视化、流程管理、作业管理等方面的支持。而这一切…

Shell编程中的循环语句和函数

一、for循环语句 当面对各种列表重复任务时&#xff0c;使用简单的if语句已经难以满足需求&#xff0c;这时就需要for循环语句。for语句的结构为&#xff1a; for 变量 in 取值列表 do 命令序列 done 使用for循环语句时&#xff0c;需要指定一个变量及取值列表&#xff0c;针对…

B站pink老师HTML5基础(一)

文章目录 一、网页1.什么是网页2.什么是HTML二、常用浏览器 三、Web标准四、HTML标签1.HTML基本结构标签 五、快捷键六、常用标签1.标题标签2.段落和换行标签3.文本格式化标签4.div标签和span标签5.图像标签6.图像路径7.超链接标签8.特殊字符 一、网页 1.什么是网页 2.什么是H…

【Postman接口测试】第二节.Postman界面功能介绍(上)

文章目录 前言一、Postman前言介绍二、Postman界面导航说明三、使用Postman发送第一个请求四、Postman 基础功能介绍 4.1 常见类型的接口请求 4.1.1 查询参数的接口请求 4.1.2 表单类型的接口请求 4.1.3 上传文件的表单请求 4.1.4 JSON 类…

New Phytologist:杨树特有miRNA在调控杨树抗旱中的分子机制

2024年3月6日&#xff0c;林木遗传育种全国重点实验室、北京林业大学生物科学与技术学院尹伟伦与夏新莉教授课题组在New Phytologist&#xff08;中科院一区&#xff0c;影响因子9.4&#xff09;期刊发表了题为“The miR6445-NAC029 module regulates drought tolerance by reg…

iec61850通信协议是什么

EC 61850是国际电工委员会&#xff08;IEC&#xff09;制定的一个用于电力系统自动化的国际标准。该协议广泛应用于变电站自动化、配电网自动化和智能电网等领域&#xff0c;旨在实现不同设备和系统之间的互操作性和高效通信。本文将详细介绍IEC 61850通信协议的特点、架构、关…

N进制计数器【02】

大容量N进制计数器 集成计数器容量的扩展 集成计数器级联扩展容量 【例1】由两片 74LS161 级联组成 256 进制&#xff08;8位二进制&#xff09;同步加法计数器 【解】级联时&#xff0c;外加时钟信号同时接到各片计数器的时钟输入端&#xff0c;用前级计数器的进位输出 C…

使用ssh连接ubuntu

一、下载连接工具 常见的连接工具右fianlshell、xshell等等。在本文章中使用的finalshell&#xff0c;工具可以去官网上下载&#xff0c;官网下载。 二、Ubuntu中配置shh 1、使用下面指令更新软件包&#xff08;常用于下载安装或更新软件时使用&#xff0c;更新到最新的安装…

如何取消公众号的在线客服绑定授权

1&#xff0c;功能设置 2&#xff0c;公众号设置 3&#xff0c;查看详情&#xff0c;取消

Excel表格保护密码遗忘怎么办?三秒钟破解密码,轻松解锁!

在我们的日常工作中&#xff0c;Excel表格是一个非常实用的工具&#xff0c;但在某些情况下&#xff0c;我们可能会遇到密码忘记的问题&#xff0c;或者在尝试打开或删除文件时被锁定。别担心&#xff0c;这里有三个简单的解决方法来帮助您解决问题。 一、尝试默认密码或常见密…

香橙派 AIpro 昇腾 Ascend C++ 分类模型适配

香橙派 AIpro 昇腾 Ascend C 分类模型适配 flyfish 文章目录 香橙派 AIpro 昇腾 Ascend C 分类模型适配前言一、PyTorch官网resnet模型处理方式1、PyTorch模型 导出 onnx格式2、完整测试 输出top1结果3、完整测试 输出top5结果 二、YOLOv8官网resnet模型Python处理方式三、昇腾…

摸鱼大数据——Hive表操作——分区表

1、介绍 特点: 分区表会在HDFS上产生目录。查询数据的时候使用分区字段筛选数据&#xff0c;可以避免全表扫描&#xff0c;从而提升查询效率 注意: 如果是分区表&#xff0c;在查询数据的时候&#xff0c;如果没有使用分区字段&#xff0c;它回去进行全表扫描&#xff0c;会降低…

【哈希】闭散列的线性探测和开散列的哈希桶解决哈希冲突(C++两种方法模拟实现哈希表)(1)

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; C进阶 &#x1f389;其它专栏&#xff1a; C初阶 | Linux | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 哈希函数与哈希 之 闭散列的线性探测解决哈希冲突 的相关内容。 如…

【Elasticsearch】Centos7安装Elasticsearch、kibana、IK分词

目录 本文安装包下载地址注意安装elasticsearch1.上传文件2.解压elasticsearch-6.3.1.tar.gz3.开启远程连接权限4.修改其他配置[root用户操作]5.重启虚拟机6.启动es7.外部访问 安装kibana-61.解压2.配置3.启动kibana4.访问5.在开发工具中做数据的增删改查操作 安装IK分词1.wind…

vue3(一):Vue3简介、创建vue3工程、Vue3中的响应式

目录 一.Vue3简介 1.性能提升 2.源码升级 3.拥抱ts 4.新特性 &#xff08;1&#xff09;Composition API&#xff08;组合API&#xff09;&#xff1a; &#xff08;2&#xff09;新的内置组件&#xff1a; &#xff08;3&#xff09;其他改变&#xff1a; 二.创建vue…

iOS推送证书过期处理

苹果推送证书的有效期都是一年&#xff0c;将要过期的时候&#xff0c;苹果官方会发邮件提醒。 一、过期 在电脑上找到并打开其它->钥匙串访问&#xff1b; 我的证书可以看到各个App的推送证书&#xff0c;如果过期了&#xff0c;显示红色X 二、重新创建 1、登陆apple开…