SQL 使用场景(二)
场景描述
根据上一行填充本行的空白栏位[1]。这是在参考资料中提出的一个问题,它给出了在 SQL Server 数据库中的解决方案,现在要在 PostgreSQL 中来模拟并解决它。
假设在 PostgreSQL 12.10 的库中有一个用户的考试成绩表
1 | create table t_score ( |
在成绩表中有如下的一些测试数据
1 | -- 用户 11 的考试成绩 |
我们期望写一句 SQL 查询出用户 11 的成绩,在没有考试的月份的成绩为最近一次有考试的月份的成绩,下面是我们期望的结果
在结果中有成绩的月份都用红色进行了标记,其他没有考试的月份的成绩为最近一次有考试成绩的月份的成绩。比如在 202210 月没有考试,则该月的成绩为 202209 月的成绩,一直到 202302 月。又比如 202305 月没有考试,则该月的成绩为 202304 月的成绩。
场景分析
1 | -- 递归是针对名称为 t_score_b 的 CTE |
分配行号
t_score_a
虽然有 recursive
修饰,但它是一个普通的 CTE,它的结果如下
非递归项
t_score_b
是递归的 CTE,它的非递归项的执行结果为
第一次递归
union all
保留所有结果行,非递归项的结果作为 t_score_b
的结果集的一部分,并且将结果放到一个临时的工作表[2]中,此时工作表不为空,开始计算递归项,用工作表的内容替换递归自引用的 t_score_b
,此时递归项的处理过程大致如下图
将第一次递归的结果加入 t_score_b
的结果集中得到如下的结果集
这个结果集就是第一次递归(迭代)后 t_score_b
的结果集。
第二次递归
将第一次递归的结果放到一个临时的中间表中,用中间表的内容替换工作表的内容,然后清空中间表,开始进行第二次递归。此时工作表不为空计算递归项
将第二次递归的结果加入 t_score_b
的结果集中得到如下的结果集
这个结果集就是第二次递归(迭代)后 t_score_b
的结果集。
第三次递归
将第二次递归的结果放到一个临时的中间表中,用中间表的内容替换工作表的内容,然后清空中间表,开始进行第三次递归。此时工作表不为空计算递归项
将第三次递归的结果加入 t_score_b
的结果集中得到如下的结果集
这个结果集就是第三次递归(迭代)后 t_score_b
的结果集。
第四次递归
将第三次递归的结果放到一个临时的中间表中,用中间表的内容替换工作表的内容,然后清空中间表,开始进行第四次递归。此时工作表不为空计算递归项
将第四次递归的结果加入 t_score_b
的结果集中得到如下的结果集
这个结果集就是第四次递归(迭代)后 t_score_b
的结果集。
第五次递归
将第四次递归的结果放到一个临时的中间表中,用中间表的内容替换工作表的内容,然后清空中间表,开始进行第五次递归。此时工作表不为空计算递归项
将第五次递归的结果加入 t_score_b
的结果集中得到如下的结果集
这个结果集就是第五次递归(迭代)后 t_score_b
的结果集。
第六次递归
将第五次递归的结果放到一个临时的中间表中,用中间表的内容替换工作表的内容,然后清空中间表,开始进行第六次递归。此时工作表不为空计算递归项
第六次递归的结果是一个空结果,为了保持完整性仍然把它们加入 t_score_b
的结果集中得到如下的结果集
这个结果集就是第六次递归(迭代)后 t_score_b
的结果集,可以看到和第五次递归后的结果集是一样的。
结束递归
将第六次递归的结果(空结果)放到一个临时的中间表中,用这个中间表的内容替换工作表的内容,然后清空中间表,此时工作表为空结束递归。
查询结果
order by exam_time
将第六次(第五次)的结果按考试时间排序,select exam_time, score
排除人为添加的 row_no
列即最终结果。