add remove split language show hidden hide
db<>fiddle
donate feedback about
By using db<>fiddle, you agree to license everything you submit by Creative Commons CC0.
version
PostgreSQL 15.0 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-10), 64-bit
SELECT 1
CREATE TABLE
id is_skippable prev next
A1 f null A2
A2 t A1 A3
A3 f A2 null
B1 f null B2
B2 f B1 null
INSERT 0 5
id prev next
A1 null A3
A3 A1 null
B1 null B2
B2 B1 null
SELECT 4
id prev next
A1 null A3
A3 A1 null
B1 null B2
B2 B1 null
SELECT 4
id prev next
A1 null A3
A3 A1 null
B1 null B2
B2 B1 null
SELECT 4
now setseed
2024-05-07 19:10:57.467445+01
SELECT 1
INSERT 0 10000
CREATE INDEX
VACUUM
QUERY PLAN
Seq Scan on public.node n1 (cost=0.00..1486771.12 rows=7982 width=69) (actual time=0.104..263.144 rows=7982 loops=1)
  Output: n1.id, (SubPlan 2), (SubPlan 4)
  Filter: (NOT n1.is_skippable)
  Rows Removed by Filter: 2023
  SubPlan 2
    -> Limit (cost=93.08..93.12 rows=1 width=32) (actual time=0.014..0.014 rows=1 loops=7982)
          Output: cte_1.id
          CTE cte
            -> Recursive Union (cost=0.29..93.08 rows=21 width=44) (actual time=0.009..0.012 rows=1 loops=7982)
                  -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2 (cost=0.29..4.30 rows=1 width=44) (actual time=0.007..0.007 rows=1 loops=7982)
                        Output: n2.id, n2.prev, n2.is_skippable, false, ARRAY[ROW(n2.prev)]
                        Index Cond: (n2.id = n1.prev)
                        Heap Fetches: 0
                  -> Nested Loop (cost=0.29..8.84 rows=2 width=44) (actual time=0.008..0.008 rows=1 loops=2358)
                        Output: n2_1.id, n2_1.prev, n2_1.is_skippable, CASE WHEN (ROW(n2_1.prev) = ANY (cte.path)) THEN true ELSE false END, array_cat(cte.path, ARRAY[ROW(n2_1.prev)])
                        Inner Unique: true
                        -> WorkTable Scan on cte (cost=0.00..0.20 rows=2 width=64) (actual time=0.000..0.000 rows=1 loops=2358)
                              Output: cte.path, cte.prev
                              Filter: (cte.is_skippable AND (NOT cte.is_cycle))
                        -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2_1 (cost=0.29..4.30 rows=1 width=11) (actual time=0.006..0.006 rows=1 loops=1912)
                              Output: n2_1.id, n2_1.is_skippable, n2_1.prev, n2_1.next
                              Index Cond: (n2_1.id = cte.prev)
                              Heap Fetches: 0
          -> CTE Scan on cte cte_1 (cost=0.00..0.42 rows=10 width=32) (actual time=0.014..0.014 rows=1 loops=7982)
                Output: cte_1.id
                Filter: (NOT cte_1.is_skippable)
                Rows Removed by Filter: 0
  SubPlan 4
    -> Limit (cost=93.08..93.12 rows=1 width=32) (actual time=0.016..0.016 rows=1 loops=7982)
          Output: cte_3.id
          CTE cte
            -> Recursive Union (cost=0.29..93.08 rows=21 width=44) (actual time=0.010..0.014 rows=1 loops=7982)
                  -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2_2 (cost=0.29..4.30 rows=1 width=44) (actual time=0.008..0.008 rows=1 loops=7982)
                        Output: n2_2.id, n2_2.next, n2_2.is_skippable, false, ARRAY[ROW(n2_2.next)]
                        Index Cond: (n2_2.id = n1.next)
                        Heap Fetches: 0
                  -> Nested Loop (cost=0.29..8.84 rows=2 width=44) (actual time=0.008..0.009 rows=1 loops=2313)
                        Output: n2_3.id, n2_3.next, n2_3.is_skippable, CASE WHEN (ROW(n2_3.next) = ANY (cte_2.path)) THEN true ELSE false END, array_cat(cte_2.path, ARRAY[ROW(n2_3.next)])
                        Inner Unique: true
                        -> WorkTable Scan on cte cte_2 (cost=0.00..0.20 rows=2 width=64) (actual time=0.000..0.000 rows=1 loops=2313)
                              Output: cte_2.path, cte_2.next
                              Filter: (cte_2.is_skippable AND (NOT cte_2.is_cycle))
                        -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2_3 (cost=0.29..4.30 rows=1 width=11) (actual time=0.006..0.006 rows=1 loops=1946)
                              Output: n2_3.id, n2_3.is_skippable, n2_3.prev, n2_3.next
                              Index Cond: (n2_3.id = cte_2.next)
                              Heap Fetches: 0
          -> CTE Scan on cte cte_3 (cost=0.00..0.42 rows=10 width=32) (actual time=0.015..0.015 rows=1 loops=7982)
                Output: cte_3.id
                Filter: (NOT cte_3.is_skippable)
                Rows Removed by Filter: 0
Planning Time: 1.275 ms
Execution Time: 264.227 ms
EXPLAIN
QUERY PLAN
Seq Scan on public.node n1 (cost=0.00..3546401.20 rows=7982 width=69) (actual time=0.066..366.128 rows=7982 loops=1)
  Output: n1.id, COALESCE((SubPlan 2), n1.prev), COALESCE((SubPlan 4), n1.next)
  Filter: (NOT n1.is_skippable)
  Rows Removed by Filter: 2023
  SubPlan 2
    -> Limit (cost=222.14..222.14 rows=1 width=36) (actual time=0.027..0.027 rows=1 loops=7982)
          Output: cte_1.prev, cte_1.i
          CTE cte
            -> Recursive Union (cost=0.29..221.86 rows=11 width=42) (actual time=0.010..0.022 rows=1 loops=7982)
                  -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2 (cost=0.29..4.30 rows=1 width=42) (actual time=0.007..0.008 rows=1 loops=7982)
                        Output: 1, n2.prev, false, ARRAY[ROW(n2.prev)]
                        Index Cond: (n2.id = n1.prev)
                        Heap Fetches: 0
                  -> Nested Loop (cost=0.29..21.73 rows=1 width=42) (actual time=0.009..0.009 rows=0 loops=9821)
                        Output: (cte.i + 1), n2_1.prev, CASE WHEN (ROW(n2_1.prev) = ANY (cte.path)) THEN true ELSE false END, array_cat(cte.path, ARRAY[ROW(n2_1.prev)])
                        Inner Unique: true
                        -> WorkTable Scan on cte (cost=0.00..0.20 rows=5 width=68) (actual time=0.000..0.000 rows=1 loops=9821)
                              Output: cte.i, cte.path, cte.prev
                              Filter: (NOT cte.is_cycle)
                        -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2_1 (cost=0.29..4.30 rows=1 width=10) (actual time=0.007..0.007 rows=0 loops=9375)
                              Output: n2_1.id, n2_1.is_skippable, n2_1.prev, n2_1.next
                              Index Cond: (n2_1.id = cte.prev)
                              Filter: n2_1.is_skippable
                              Rows Removed by Filter: 1
                              Heap Fetches: 0
          -> Sort (cost=0.28..0.30 rows=11 width=36) (actual time=0.026..0.026 rows=1 loops=7982)
                Output: cte_1.prev, cte_1.i
                Sort Key: cte_1.i DESC
                Sort Method: quicksort Memory: 25kB
                -> CTE Scan on cte cte_1 (cost=0.00..0.22 rows=11 width=36) (actual time=0.011..0.023 rows=1 loops=7982)
                      Output: cte_1.prev, cte_1.i
  SubPlan 4
    -> Limit (cost=222.14..222.14 rows=1 width=36) (actual time=0.016..0.016 rows=0 loops=7982)
          Output: cte_3.next, cte_3.i
          CTE cte
            -> Recursive Union (cost=0.29..221.86 rows=11 width=42) (actual time=0.009..0.012 rows=0 loops=7982)
                  -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2_2 (cost=0.29..4.30 rows=1 width=42) (actual time=0.007..0.007 rows=0 loops=7982)
                        Output: 1, n2_2.next, false, ARRAY[ROW(n2_2.next)]
                        Index Cond: (n2_2.id = n1.next)
                        Filter: n2_2.is_skippable
                        Rows Removed by Filter: 1
                        Heap Fetches: 0
                  -> Nested Loop (cost=0.29..21.73 rows=1 width=42) (actual time=0.002..0.002 rows=0 loops=8375)
                        Output: (cte_2.i + 1), n2_3.next, CASE WHEN (ROW(n2_3.next) = ANY (cte_2.path)) THEN true ELSE false END, array_cat(cte_2.path, ARRAY[ROW(n2_3.next)])
                        Inner Unique: true
                        -> WorkTable Scan on cte cte_2 (cost=0.00..0.20 rows=5 width=68) (actual time=0.000..0.000 rows=0 loops=8375)
                              Output: cte_2.i, cte_2.path, cte_2.next
                              Filter: (NOT cte_2.is_cycle)
                        -> Index Only Scan using node_id_is_skippable_prev_next_idx on public.node n2_3 (cost=0.29..4.30 rows=1 width=10) (actual time=0.007..0.007 rows=0 loops=1946)
                              Output: n2_3.id, n2_3.is_skippable, n2_3.prev, n2_3.next
                              Index Cond: (n2_3.id = cte_2.next)
                              Filter: n2_3.is_skippable
                              Rows Removed by Filter: 1
                              Heap Fetches: 0
          -> Sort (cost=0.28..0.30 rows=11 width=36) (actual time=0.015..0.015 rows=0 loops=7982)
                Output: cte_3.next, cte_3.i
                Sort Key: cte_3.i DESC
                Sort Method: top-N heapsort Memory: 25kB
                -> CTE Scan on cte cte_3 (cost=0.00..0.22 rows=11 width=36) (actual time=0.010..0.013 rows=0 loops=7982)
                      Output: cte_3.next, cte_3.i
Planning Time: 0.944 ms
Execution Time: 368.270 ms
EXPLAIN