Как подсчитать количество записей в цепочке?

У меня ессть упрощенная таблица перемещения пассажирских поездов, которые показывают его перемещение из города в город:

create table testt (
    ID NUMBER,
    VALST VARCHAR2(255),
    VALEND VARCHAR2(255),
    NEXTID NUMBER
)

   ID VALST  VALEND NEXTID
----- ----- ------- ------
    1   MOS     SAN      2
    2   SAN     MOS      
    3   MOS     EKB      4
    4   EKB     RYA      5
    5   RYA     SAN      6
    6   SAN     MOS      
    7   MOS     KRA      8
    8   KRA     VOR      9
    9   VOR     MOS      

Работает она следующим образом: В ячейке VALST показано, откуда выезжает поезд, в VALEND - куда он приезжает. В ячейке NEXTID написан идентификатор следующего рейса. То есть, например, по ID 1 поезд поедет сначала из MOS в SAN, затем, в следующем рейсе под ID 2 он поедет из SAN в MOS, и цепочка на этом закончится, так как нет следующего идентификатора.

Задача состоит в следующем: определить количество рейсов в цепочке, то есть, когда поезд выезжает из MOS, и в итоге возвращается в MOS.

В примере сверху из ID 1 размер цепочки будет равен два, а из ID 3 цепочка будет равна четырем.

Пробовал через функцию LEAD, но, к сожалению, не получилось, прошу помощи.


Ответы (2 шт):

Автор решения: Akina

Вот так это выглядит в MySQL (извини, для ORACLE не стал делать, там сущее мучение с вводом данных в fiddle, а за диагностику проблем так и вовсе руки оторвать, но выглядеть будет практически так же):

WITH RECURSIVE
cte1 AS ( SELECT ID, VALST, VALEND, NEXTID, 1 AS length, ROW_NUMBER() OVER (ORDER BY ID) chain
          FROM testt
          WHERE NEXTID IS NULL
          UNION ALL
          SELECT testt.ID, testt.VALST, cte1.VALEND, testt.NEXTID, cte1.length + 1, cte1.chain
          FROM testt
          JOIN cte1 ON testt.NEXTID = cte1.ID ),
cte2 AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY chain ORDER BY length DESC) rn
          FROM cte1 )
SELECT ID, VALST, length 
FROM cte2
WHERE rn = 1;

https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=a41425e1b0c3cb532c410351a95f8ace

→ Ссылка
Автор решения: 0xdb

Данные представляют собой обратную иерархию (reverse hierarchy).

Решатся простым иерархическим запросом (на db<>fiddle):

select valst, level numoftrips, sys_connect_by_path (valst, ' >> ') trips
from testt t
where connect_by_isleaf = 1
start with nextid is null 
connect by nextid = prior id

Результат:

VALST  NUMOFTRIPS TRIPS                            
------ ---------- --------------------------------
MOS             2  >> SAN >> MOS                  
MOS             4  >> SAN >> RYA >> EKB >> MOS    
MOS             3  >> VOR >> KRA >> MOS           
→ Ссылка