Search code examples
sqlsql-serversql-server-2016sql-server-2017

Path between two points


I have a table with connections between two points:

enter image description here

RidgeId PointId1    PointId2
1   1   2
2   3   2
3   17  10
4   18  10
6   18  11
7   11  3
8   4   1
9   13  4
10  16  13
11  15  16
12  5   15
13  19  5
14  20  19
15  21  20
16  8   21
17  12  8
18  6   12

I would like to find path between two points. f. egz.

from 3 to 10

  • 3, 11
  • 11, 18
  • 18, 10

How to write a query to get this result?

EDit:

How to add information about consistent connection with original row like this:

enter image description here


Solution

  • You could use a recursive CTE for this. Since you could have multiple routes, the easiest way to return a resultset is to return it as JSON

    • There are two anchors and two recursions because we can go from Point1 to Point2 or v.v. for each node
    DECLARE @start int = 3;
    DECLARE @end int = 10;
    
    WITH cte AS (
        SELECT
          Point2 = p.PointId2,
          p.RidgeId,
          Json = (
              SELECT p.RidgeId, p.PointId1, p.PointId2
              FOR JSON PATH
          )
        FROM Points p
        WHERE p.PointId1 = @start
        
        UNION ALL
        
        SELECT
          Point2 = p.PointId1,
          p.RidgeId,
          Json = (
              SELECT p.RidgeId, p.PointId1, p.PointId2
              FOR JSON PATH
          )
        FROM Points p
        WHERE p.PointId2 = @start
        
        UNION ALL
        
        SELECT
          Point2 = p.PointId2,
          p.RidgeId,
          Json = JSON_MODIFY(cte.Json, 'append $', JSON_QUERY((
              SELECT p.RidgeId, p.PointId1, p.PointId2
              FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
          )))
        FROM Points p
        JOIN cte ON cte.Point2 = p.PointId1
        WHERE p.RidgeId <> cte.RidgeId
    
        UNION ALL
        
        SELECT
          Point2 = p.PointId1,
          p.RidgeId,
          Json = JSON_MODIFY(cte.Json, 'append $', JSON_QUERY((
              SELECT p.RidgeId, p.PointId1, p.PointId2
              FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
          )))
        FROM Points p
        JOIN cte ON cte.Point2 = p.PointId2
        WHERE p.RidgeId <> cte.RidgeId
    )
    
    SELECT Json
    FROM cte
    WHERE cte.Point2 = @end;
    
    Result
    [{"RidgeId":7,"PointId1":11,"PointId2":3}, {"RidgeId":6,"PointId1":18,"PointId2":11}, {"RidgeId":4,"PointId1":18,"PointId2":10}]

    db<>fiddle

    This does not deal well with a loop in the nodes. Ideally you should check for no loops by breaking out the JSON in each recursive section and checking we are not going back on ourselves