sql—沿dag的顶点行

cygmwpex  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(359)

目标是从

-- a → b → c → d
-- ↓ ↘   ↘
-- g   e → f
-- ↓
-- h
dag (vertex, successor) as (values
    ('alpha', 'bravo'),
    ('bravo', 'charlie'),
    ('charlie', 'delta'),
    ('delta', null),
    ('alpha', 'echo'),
    ('echo', 'foxtrot'),
    ('bravo', 'foxtrot'),
    ('foxtrot', null),
    ('alpha', 'golf'),
    ('golf', 'hotel'),
    ('hotel', null)
)

进入之内

'id_foo', 0, 'alpha'
'id_foo', 1, 'bravo'
'id_foo', 2, 'charlie'
'id_foo', 3, 'delta'
'id_bar', 0, 'alpha'
'id_bar', 1, 'bravo'
'id_bar', 2, 'foxtrot'
'id_qux', 0, 'alpha'
'id_qux', 1, 'echo'
'id_qux', 2, 'foxtrot'
'id_fno', 0, 'alpha'
'id_fno', 1, 'golf'
'id_fno', 2, 'hotel'

我可以分两步做。

with recursive intermediate as (
    select * from (
        with recursive
            dag (vertex, successor) as (values
                ('alpha', 'bravo'),
                ('bravo', 'charlie'),
                ('charlie', 'delta'),
                ('delta', null),
                ('alpha', 'echo'),
                ('echo', 'foxtrot'),
                ('bravo', 'foxtrot'),
                ('foxtrot', null),
                ('alpha', 'golf'),
                ('golf', 'hotel'),
                ('hotel', null)
            ),
            cte as (
                select
                        vertex,
                        successor,
                        1 as length,
                        vertex as path
                    from dag where successor is null
                union all
                select
                        dag.vertex,
                        dag.successor,
                        length + 1,
                        dag.vertex||'→'||path
                    from dag join cte
                    where dag.successor = cte.vertex
            )
        select
            length, path
            from cte
            where vertex = 'alpha'
        -- 3, 'alpha→bravo→charlie→delta'
        -- 3, 'alpha→bravo→foxtrot'
        -- 3, 'alpha→echo→foxtrot'
        -- 4, 'alpha→golf→hotel'
    )
),
cte as (
    select
            length,
            path,
            -1 as "index",
            '' as vertex,
            path||'→' as rest
        from intermediate
    union all
    select
            length,
            path,
            "index" + 1,
            substr(rest, 0, instr(rest, '→')),
            substr(rest, instr(rest, '→') + 1)
        from cte
        where rest != ''
)
select length, path, "index", vertex from cte
where vertex != ''
order by path, "index";

我认为这样做是浪费。有没有可能没有字符串连接和拆分,没有中间结果集?
目标平台是模糊的现代sql。上面的代码已经过测试,并用sqlite运行。

j2datikz

j2datikz1#

with recursive
dag (vertex, successor) as (values
    ('alpha', 'bravo'),
    ('bravo', 'charlie'),
    ('charlie', 'delta'),
    ('delta', null),
    ('alpha', 'echo'),
    ('echo', 'foxtrot'),
    ('bravo', 'foxtrot'),
    ('foxtrot', null),
    ('alpha', 'golf'),
    ('golf', 'hotel'),
    ('hotel', null)
),
head (vertex) as (
        select vertex from dag except select successor from dag
),
paths (row, depth, parent_row, vertex, successor) as (
        select row_number() over (), 1, 0::bigint, dag.vertex, dag.successor
                from head
                join dag on (dag.vertex = head.vertex)
        union all
        select row_number() over (), depth + 1, row, dag.vertex, dag.successor
                from paths
                join dag on (dag.vertex = paths.successor)
),
result (path_id, parent_row, depth, vertex) as (
        select row_number() over (), parent_row, depth, vertex
                from paths
                where successor is null
        union all
        select result.path_id, paths.parent_row, paths.depth, paths.vertex
                from result
                join paths on (paths.row = result.parent_row and paths.depth = result.depth - 1)
)
select path_id, depth, vertex
        from result
        order by 1, 2
;

特定于postgresql

with recursive
dag (vertex, successor) as (values
        ('alpha', 'bravo'),
        ('bravo', 'charlie'),
        ('charlie', 'delta'),
        ('delta', null),
        ('alpha', 'echo'),
        ('echo', 'foxtrot'),
        ('bravo', 'foxtrot'),
        ('foxtrot', null),
        ('alpha', 'golf'),
        ('golf', 'hotel'),
        ('hotel', null)
),
head (vertex) as (
        select vertex from dag except select successor from dag
),
paths (path, successor) as (
        select array[dag.vertex], dag.successor
                from head
                join dag on (dag.vertex = head.vertex)
        union all
        select array_append(paths.path, dag.vertex), dag.successor
                from paths
                join dag on (dag.vertex = paths.successor)
),
result_paths (path_id, path) as (
        select row_number() over (), path
                from paths
                where successor is null
),
result (path_id, depth, vertex) as (
        select path_id, p.nr, p.elem
                from result_paths
                left join lateral unnest (path) with ordinality as p(elem, nr) on true
)
select * from result
        order by 1,2
;

相关问题