Flights Table
We will start from scratch in MySQL.
Let’s create a Flights table and populate it with dummy data.
SHOW tables;
SELECT * from Flights;
DROP TABLE Flights;
-- Create the table
CREATE TABLE Flights (
FlightID INT,
Origin CHAR(1),
Destination CHAR(1)
);
-- Insert data into the table
INSERT INTO Flights (FlightID, Origin, Destination) VALUES
(1, 'A', 'B'),
(1, 'B', 'C'),
(2, 'D', 'E'),
(3, 'F', 'G'),
(3, 'G', 'H'),
(3, 'H', 'I'),
(4, 'A', 'N'),
(4, 'M', 'D'),
(4, 'D', 'A');
Results of the Insert Query
FlightID |
Origin |
Destination |
1 |
A |
B |
1 |
B |
C |
2 |
D |
E |
3 |
F |
G |
3 |
G |
H |
3 |
H |
I |
4 |
A |
N |
4 |
M |
D |
4 |
D |
A |
Base Query
SELECT FlightID, Origin, Destination,
CAST(CONCAT(Origin, ',', Destination) AS CHAR(100)) AS Route,
1 AS Step
FROM Flights
WHERE Origin NOT IN (
SELECT Destination
FROM Flights AS subFlights
WHERE subFlights.FlightID = Flights.FlightID
)
Output
FlightID |
Origin |
Destination |
Route |
Step |
1 |
A |
B |
A,B |
1 |
2 |
D |
E |
D,E |
1 |
3 |
F |
G |
F,G |
1 |
4 |
M |
D |
M,D |
1 |
Using Left join
SELECT
f1.FlightID,
f1.Origin,
f1.Destination,
CAST(CONCAT(f1.Origin, ',', f1.Destination) AS CHAR(100)) AS Route,
1 AS Step
FROM
Flights AS f1
LEFT JOIN
Flights AS f2 ON f1.Origin = f2.Destination AND f1.FlightID = f2.FlightID
WHERE
f2.Destination IS NULL
Output
FlightID |
Origin |
Destination |
Route |
Step |
1 |
A |
B |
A,B |
1 |
2 |
D |
E |
D,E |
1 |
3 |
F |
G |
F,G |
1 |
4 |
M |
D |
M,D |
1 |
First Iteration
The first iteration will take the output of the base query as the input and generate row entries.
WITH RECURSIVE FlightRoutes AS (
SELECT FlightID, Origin, Destination, CAST(CONCAT(Origin,',',Destination) AS CHAR(100)) As Route, 1 as Step
FROM Flights
WHERE Origin NOT IN (
SELECT Destination
FROM Flights AS subFlights
WHERE subFlights.FlightID = Flights.FlightID
)
UNION ALL
SELECT fr.FlightID, fr.Origin, t.Destination, CAST(CONCAT(fr.Route, ',', t.Destination) AS CHAR(100)), fr.Step+1 as Step
FROM FlightRoutes fr
JOIN Flights t ON fr.FlightID = t.FlightID AND fr.Destination = t.Origin
WHERE POSITION(t.Destination IN fr.Route) = 0
AND fr.Step <2
)
select *
from FlightRoutes;
FlightID |
Origin |
Destination |
Route |
Step |
1 |
A |
B |
A,B |
1 |
2 |
D |
E |
D,E |
1 |
3 |
F |
G |
F,G |
1 |
4 |
M |
D |
M,D |
1 |
Rows getting generated at the First Iteration
FlightID |
Origin |
Destination |
Route |
Step |
1 |
A |
C |
A,B,C |
2 |
3 |
F |
H |
F,G,H |
2 |
4 |
M |
A |
M,D,A |
2 |
Overall Output at the end of first iteration
The row entries generated will be appended to the output of the base query.
FlightID |
Origin |
Destination |
Route |
Step |
1 |
A |
B |
A,B |
1 |
2 |
D |
E |
D,E |
1 |
3 |
F |
G |
F,G |
1 |
4 |
M |
D |
M,D |
1 |
1 |
A |
C |
A,B,C |
2 |
3 |
F |
H |
F,G,H |
2 |
4 |
M |
A |
M,D,A |
2 |
Second Iteration
- The second iteration will take the generated row entries/records of the first iteration as input.
- The inputs to the second iteration will only be the records fetched as output from the first iteration.
WITH RECURSIVE FlightRoutes AS (
SELECT FlightID, Origin, Destination, CAST(CONCAT(Origin,',',Destination) AS CHAR(100)) As Route, 1 as Step
FROM Flights
WHERE Origin NOT IN (
SELECT Destination
FROM Flights AS subFlights
WHERE subFlights.FlightID = Flights.FlightID
)
UNION ALL
SELECT fr.FlightID, fr.Origin, t.Destination, CAST(CONCAT(fr.Route, ',', t.Destination) AS CHAR(100)), fr.Step+1 as Step
FROM FlightRoutes fr
JOIN Flights t ON fr.FlightID = t.FlightID AND fr.Destination = t.Origin
WHERE POSITION(t.Destination IN fr.Route) = 0
AND fr.Step <3
)
select *
from FlightRoutes;
FlightID |
Origin |
Destination |
Route |
Step |
1 |
A |
C |
A,B,C |
2 |
3 |
F |
H |
F,G,H |
2 |
4 |
M |
A |
M,D,A |
2 |
Rows getting generated at the Second Iteration
FlightID |
Origin |
Destination |
Route |
Step |
3 |
F |
I |
F,G,H,I |
3 |
4 |
M |
N |
M,D,A,N |
3 |
The newly generated rows will get appended to the continuously expanding table in the recursive CTE.
Overall Output at the end of second iteration
FlightID |
Origin |
Destination |
Route |
Step |
1 |
A |
B |
A,B |
1 |
2 |
D |
E |
D,E |
1 |
3 |
F |
G |
F,G |
1 |
4 |
M |
D |
M,D |
1 |
1 |
A |
C |
A,B,C |
2 |
3 |
F |
H |
F,G,H |
2 |
4 |
M |
A |
M,D,A |
2 |
3 |
F |
I |
F,G,H,I |
3 |
4 |
M |
N |
M,D,A,N |
3 |
WITH RECURSIVE FlightRoutes AS (
-- Anchor Member
SELECT FlightID, Origin, Destination,
CAST(CONCAT(Origin, ',', Destination) AS CHAR(100)) AS Route,
1 AS Step
FROM Flights
WHERE Origin NOT IN (
SELECT Destination
FROM Flights AS subFlights
WHERE subFlights.FlightID = Flights.FlightID
)
UNION ALL
-- Recursive Member
SELECT fr.FlightID, fr.Origin, t.Destination,
CAST(CONCAT(fr.Route, ',', t.Destination) AS CHAR(100)),
fr.Step + 1 AS Step
FROM FlightRoutes fr
JOIN Flights t ON fr.FlightID = t.FlightID AND fr.Destination = t.Origin
WHERE POSITION(t.Destination IN fr.Route) = 0
AND fr.Step < 3
)
SELECT
FlightID,
Origin,
Destination AS FinalDestination,
Route,
Step AS CountOfConnectingFlights
FROM (
SELECT
FlightID,
Origin,
Destination,
Route,
Step,
ROW_NUMBER() OVER (PARTITION BY FlightID ORDER BY FlightID, Step DESC) AS RowNumber
FROM FlightRoutes
) AS tbl
WHERE RowNumber = 1;
Output
FlightID |
Origin |
Destination |
Route |
Step |
2 |
D |
E |
D,E |
1 |
1 |
A |
C |
A,B,C |
2 |
3 |
F |
I |
F,G,H,I |
3 |
4 |
M |
N |
M,D,A,N |
3 |
Summary
The base query retrieves the initial set of records from the source table based on specified conditions. The recursive portion then takes the output of the base query and generates additional row entries. These new entries are appended to the output of the base query. In each iteration, the input for the next iteration consists of the records generated in the previous iteration. This process continues, and the overall output table grows with each iteration. The number of iterations can be separately tracked, providing a clear view of the flow of input and output across subsequent iterations.