递归查询替换空值

问题描述 投票:0回答:1

如下表,我需要进行查询,将“time”(分钟数,整数)的空值替换为与前后值一致的值,并按“esz”分区并按“升序”排序秩”。在示例中,数据已经排序。 理想情况下:

  • 当存在时,该值应尽可能接近前一个值(+1)
  • 如果前面没有值(排名 1),则该值应尽可能接近后面的值(-1)
  • 如果前后值之间的差异不足(例如0或1),则该值必须等于两者中较小的一个。
  • 该值绝不能小于前一个值,也不能大于后一个值。 没有“esz”只能有空的“时间”值 我已经尝试过 LAG() 和 LEAD(),但是,由于空值可以在任何地方,我认为这不是最好的选择。 我想我必须使用递归查询,但我做不到。 谁能帮我找到解决方案吗? 非常感谢你
id esz 代码 排名 时间
1 1 “SOM” 1 5
2 1 “奎” 2
3 1 “版本” 3 10
4 1 “国家安全委员会” 4 15
5 1 “3SM” 99
6 2 “奎” 1 7
7 2 “版本” 2
8 2 “SOM” 3
9 2 “国家安全委员会” 4 12
10 2 “3SM” 99
11 3 “国家安全委员会” 1
12 3 “版本” 2
13 3 “奎” 3 11
14 3 “SOM” 4 12
15 3 “3SM” 99
16 4 “SOM” 1 2
17 4 “奎” 2
18 4 “国家安全委员会” 3 3
19 4 “版本” 4
20 4 “3SM” 99
21 5 “国家安全委员会” 1
22 5 “SOM” 2 4
23 5 “版本” 3
24 5 “奎” 4 7
25 5 “3SM” 99

输出示例:

id esz 代码 排名 时间
1 1 “SOM” 1 5
2 1 “奎” 2 6
3 1 “版本” 3 10
4 1 “国家安全委员会” 4 15
5 1 “3SM” 99 16
6 2 “奎” 1 7
7 2 “版本” 2 8
8 2 “SOM” 3 9
9 2 “国家安全委员会” 4 12
10 2 “3SM” 99 13
11 3 “国家安全委员会” 1 9
12 3 “版本” 2 10
13 3 “奎” 3 11
14 3 “SOM” 4 12
15 3 “3SM” 99 13
16 4 “SOM” 1 2
17 4 “奎” 2 2
18 4 “国家安全委员会” 3 3
19 4 “版本” 4 4
20 4 “3SM” 99 5
21 5 “国家安全委员会” 1 3
22 5 “SOM” 2 4
23 5 “版本” 3 5
24 5 “奎” 4 7
25 5 “3SM” 99 8

我可以使用 LAG() 和 LEAD() 以及 CASE 处理表中的大多数情况。但我对其中一些仍然有问题:例如 id=11 和 id=12。要么我必须从 id=13 的值向后退,要么我必须知道在填充 id=11 时我必须留下一个可用于填充 id=12 的值。

sql recursive-query postgresql-13
1个回答
0
投票

尝试使用完整的数据集:

with data(id, esz, code, rank, time) as (
    select 1, 1, 'SOM', 1, 5 from dual union all
    select 2, 1, 'QUI', 2, NULL from dual union all
    select 3, 1, 'VER', 3, 10 from dual union all
    select 4, 1, 'NSC', 4, 15 from dual union all
    select 5, 1, '3SM', 99, NULL from dual union all
    select 6, 2, 'QUI', 1, 7 from dual union all
    select 7, 2, 'VER', 2, NULL from dual union all
    select 8, 2, 'SOM', 3, NULL from dual union all
    select 9, 2, 'NSC', 4, 12 from dual union all
    select 10, 2, '3SM', 99, NULL from dual union all
    select 11, 3, 'NSC', 1, NULL from dual union all
    select 12, 3, 'VER', 2, NULL from dual union all
    select 13, 3, 'QUI', 3, 11 from dual union all
    select 14, 3, 'SOM', 4, 12 from dual union all
    select 15, 3, '3SM', 99, NULL from dual union all
    select 16, 4, 'SOM', 1, 2 from dual union all
    select 17, 4, 'QUI', 2, NULL from dual union all
    select 18, 4, 'NSC', 3, 3 from dual union all
    select 19, 4, 'VER', 4, NULL from dual union all
    select 20, 4, '3SM', 99, NULL from dual union all
    select 21, 5, 'NSC', 1, NULL from dual union all
    select 22, 5, 'SOM', 2, 4 from dual union all
    select 23, 5, 'VER', 3, NULL from dual union all
    select 24, 5, 'QUI', 4, 7 from dual union all
    select 25, 5, '3SM', 99, NULL from dual
)
select d.id, d.esz, d.code, d.rank, 
    nvl(time, 
        case when ntime-ndelta < ptime+pdelta
        then
            ntime-ndelta
        else
            nvl(ptime+pdelta, ntime-ndelta)
        end 
    ) as time
from (
    select d.*, 
        row_number() over(partition by esz, grp order by rank) as pdelta,
        row_number() over(partition by esz, grp order by rank desc) as ndelta
    from (
        select d.*, 
            nvl(time, last_value(time ignore nulls) over(partition by esz order by rank
            rows between unbounded preceding and 1 preceding))
            as ptime,
            rank - sum(nvl2(time,null,1)) over(partition by esz order by rank) as grp,
            nvl(time, first_value(time ignore nulls) over(partition by esz order by rank
            rows between 1 following and unbounded following) )
            as ntime
        from data d
    ) d
) d
order by esz, rank
;



1   1   SOM 1   5
2   1   QUI 2   6
3   1   VER 3   10
4   1   NSC 4   15
5   1   3SM 99  16
6   2   QUI 1   7
7   2   VER 2   8
8   2   SOM 3   9
9   2   NSC 4   12
10  2   3SM 99  13
11  3   NSC 1   9
12  3   VER 2   10
13  3   QUI 3   11
14  3   SOM 4   12
15  3   3SM 99  13
16  4   SOM 1   2
17  4   QUI 2   2
18  4   NSC 3   3
19  4   VER 4   5
20  4   3SM 99  4
21  5   NSC 1   3
22  5   SOM 2   4
23  5   VER 3   6
24  5   QUI 4   7
25  5   3SM 99  8
© www.soinside.com 2019 - 2024. All rights reserved.