DECLARE @route TABLE ( route INT NOT NULL, step INT NOT NULL, destination INT NOT NULL, PRIMARY KEY (route, step) ) INSERT INTO @route VALUES (1, 1, 1), (1, 2, 2), (1, 3, 3), (1, 4, 4), (2, 1, 3), (2, 2, 4) DECLARE @gps TABLE ( vehicle INT NOT NULL, destination INT NOT NULL, ts DATETIME NOT NULL ) INSERT INTO @gps VALUES (1, 1, '2011-03-30 00:00:00'), (1, 2, '2011-03-30 00:00:01'), (1, 1, '2011-03-30 00:00:02'), (1, 3, '2011-03-30 00:00:03'), (1, 3, '2011-03-30 00:00:04'), (1, 3, '2011-03-30 00:00:05'), (1, 4, '2011-03-30 00:00:06'), (1, 1, '2011-03-30 00:00:07'), (1, 3, '2011-03-30 00:00:08'), (1, 4, '2011-03-30 00:00:09'), (1, 1, '2011-03-30 00:00:10'), (1, 2, '2011-03-30 00:00:11'), (1, 2, '2011-03-30 00:00:12'), (1, 3, '2011-03-30 00:00:13'), (1, 3, '2011-03-30 00:00:14'), (1, 4, '2011-03-30 00:00:15'), (1, 3, '2011-03-30 00:00:16'), (1, 4, '2011-03-30 00:00:17') ; WITH iteration (vehicle, destination, ts, route, edge, step, cnt) AS ( SELECT vehicle, destination, ts, route, 1, step, cnt FROM ( SELECT g.vehicle, r.destination, ts, route, step, cnt, ROW_NUMBER() OVER (PARTITION BY route, vehicle ORDER BY ts) rn FROM ( SELECT *, COUNT(*) OVER (PARTITION BY route) cnt FROM @route ) r JOIN @gps g ON g.destination = r.destination WHERE r.step = 1 ) q WHERE rn = 1 UNION ALL SELECT vehicle, destination, ts, route, edge, step, cnt FROM ( SELECT i.vehicle, r.destination, g.ts, i.route, edge + 1 AS edge, r.step, cnt, ROW_NUMBER() OVER (PARTITION BY i.route, g.vehicle ORDER BY g.ts) rn FROM iteration i JOIN @route r ON r.route = i.route AND r.step = (i.step % cnt) + 1 JOIN @gps g ON g.vehicle = i.vehicle AND g.destination = r.destination AND g.ts > i.ts ) q WHERE rn = 1 ) SELECT route, vehicle, MAX(edge / cnt) FROM iteration GROUP BY route, vehicle
Here we have two routes: (1, 2, 3, 4) and (3, 4)
The car made 2 trips along the route (1, 2, 3, 4) and 4 trips along the route (3, 4) .
It is important that each route has steps with a number starting with 1 and without spaces (although if this is not the case, you can easily get around it with the optional CTE with ROW_NUMBER() )