Station Coding Exercise from Brent Ozar

By Greg No comments

Brent Ozar recently posted a coding exercise on his site which looked fun: Query Exercise: Return Routes in the Right Order – Brent Ozar Unlimited®

As usual with Brent, the setup code worked perfectly, leaving us with the problem to solve.

First step – can we work out the next station based on the normal and override routing? Turns out we can pretty easily with a left join:

SELECT		Stations.StationName	AS FromStationName,
		NextStation.StationName AS NextStationName
FROM		Stations
LEFT JOIN	StationRoutingOverride
	   ON	StationRoutingOverride.StationFromName = Stations.StationName
LEFT JOIN	Stations AS NextStation
	   ON	(
			StationRoutingOverride.StationToName IS NOT NULL
		 AND	StationRoutingOverride.StationToName = NextStation.StationName
		)
		OR
		(
			StationRoutingOverride.StationRoutingOverrideId IS NULL
		  AND	NextStation.StationId = Stations.StationId + 1
		)

That gives us a nice simple list with all the stations and where they go next, with both the normal operation for anything without an override, and heading to the override for anything with it. I’m assuming that the StationId is always sequential, otherwise we could edit the Stations.StationId + 1 to be predictable

Next, how do we recurse the whole list to find the right places to go? A recursive CTE!
I was just going to put the above into the recursive CTE, but due to something I don’t understand in SQL, you can’t have a recursive CTE with a left join. I’m sure there’s a good reason for that, but we can easily solve it by putting it into its own CTE. You could also just dump it into a temp table for the same result – probably better performance if there was a non-trivial number of rows as you can index it.

WITH NextStation AS
(
	SELECT		Stations.StationName	AS FromStationName,
				NextStation.StationName AS NextStationName
	FROM		Stations
	LEFT JOIN	StationRoutingOverride
		   ON	StationRoutingOverride.StationFromName = Stations.StationName

   LEFT JOIN	Stations AS NextStation
		   ON	(
					StationRoutingOverride.StationToName IS NOT NULL
			  AND	StationRoutingOverride.StationToName = NextStation.StationName
				)
		   OR
				(
					StationRoutingOverride.StationRoutingOverrideId IS NULL
			  AND	NextStation.StationId = Stations.StationId + 1
				)
),
	 uglyStations AS
(
	SELECT	CAST(null AS VARCHAR(50)) AS FromStationName,
		Stations.StationName AS NextStationName
	FROM	Stations
	WHERE	Stations.StationName = 'A'
	UNION ALL
	SELECT	NextStation.FromStationName,
		NextStation.NextStationName
	FROM	NextStation
   INNER JOIN	uglyStations
	   ON	uglyStations.NextStationName = NextStation.FromStationName
)
SELECT	uglyStations.FromStationName
FROM	uglyStations
WHERE	uglyStations.FromStationName IS NOT NULL

I don’t know why I called it uglyStations, probably because I couldn’t do a left join and that annoyed me.

There’s a bunch of ways I could’ve started the recursive CTE, I went with a dummy row that said to travel from ‘null’ to start at ‘A’. There’s plenty of other ways to get this started. SQL decided to make me cast the null in first part of the union to make the data types the same.
Next up, union the first result with the CTE to make it recursive, and then finally select the answer from uglyStations. This could have used the FromStationName or the NextStationName, just change the where clause to be the same.

Full gist can be found here: gist

That was good fun, and nice to see other answers come through on Brent’s blog as well. What do you think of my approach? What would you do differently?

Leave a Reply