Консолидация 2 рекурсивных операторов CTE - PullRequest
0 голосов
/ 23 февраля 2020

В следующем операторе используются 2 отдельных рекурсивных оператора CTE для построения списка шагов от начального местоположения до конечного местоположения на основе поездок. Требуемый вывод правильный, однако мне интересно, возможно ли объединить 2 оператора CTE в один.

У меня возникла трудность, связанная с привязкой endLocation к startLocation в первой рекурсивной итерации cte1.

База данных SQL Server 2017. Я добавил SQL скрипту ниже:

[SQL Fiddle][1]

SQL Настройка схемы сервера 2017 :

Create Table TripLocation
(TripID int,
 LocationID int,
 StopOrder int
 )

 Create table FromTo
(tripID int,
  fromLocationID int,
  fromStopOrder int,
  toLocationID int,
  toStopOrder int
  )


Create table cte1Temp
(startTripID int,
 startLocationID int,
 tripID int,
 fromLocationID int,
 toLocationID int,
 step int) 

Create table cte2Temp
(startTripID int,
 startLocationID int,
 endLocationID int,
 tripID int,
 fromLocationID int,
 toLocationID int,
 step int) 


 --LIST OF LOCATIONS FOR EACH TRIP
 Insert into TripLocation
 Values
 (1,1,0),
 (1,2,1),
 (1,1,2),
 (2,2,0),
 (2,3,1),
 (2,2,2),
 (3,3,0),
 (3,4,1),
 (3,3,2)

 --LIST OF POSSIBLE TO/FROM COMBINATIONS FOR EACH TRIP BASED ON STOPORDER
  insert into FromTo
  select
FromLocation.tripID,
FromLocation.LocationID [fromLocationID],
FromLocation.StopOrder [fromStopOrder],
ToLocation.LocationID [toLocationID],
ToLocation.StopOrder [toStopOrder]
from
TripLocation FromLocation
join TripLocation ToLocation
  on FromLocation.tripID = ToLocation.tripID
  and ToLocation.StopOrder >= FromLocation.StopOrder
  and FromLocation.LocationID <> ToLocation.LocationID




;
--FIND ALL POSSIBLE END LOCATIONS FOR EACH START LOCATION IF TRIPS SHARE A COMMON LOCATION
  with cte1 as
   (
     select
     tripID [startTripID],
     fromLocationID [startLocationID],
     tripID,
     fromLocationID,
     toLocationID,
     1 [step]
     from
     FromTo

     union all

     select
     anchor.startTripID,
     anchor.startLocationID,
     member.tripID,
     member.fromLocationID,
     member.toLocationID,
     anchor.step + 1 [step]
     from
     FromTo member
     join cte1 anchor
       on anchor.toLocationID = member.fromLocationID
       and member.toLocationID <> anchor.fromLocationID
       and member.tripID <> anchor.tripID
    )


insert into cte1Temp
select
*
from
cte1

;

--GENERATE PLAN FOR EACH START LOCATION TO AN END LOCATION
with cte2 as
    (
      select
      startTripID,
      StartLocationID,
      ToLocationID [EndLocationID],
      tripID,
      FromLocationID,
      ToLocationID,
      step
      from
      cte1Temp

      union all

      select
      b.startTripID,
      b.StartLocationID,
      b.ToLocationID,
      a.tripID,
      a.FromLocationID,
      a.ToLocationID,
      a.step
      from
      cte1Temp b
      join CTE2 a
        on a.endLocationID = b.FromLocationID
        and a.startLocationID = b.startLocationID
      )


insert into cte2Temp
select
*
from
cte2

Запрос 1 :

select
*
from
cte2Temp
order by
startlocationID, endLocationID, step

Результаты :

| startTripID | startLocationID | endLocationID | tripID | fromLocationID | toLocationID | step |
|-------------|-----------------|---------------|--------|----------------|--------------|------|
|           1 |               1 |             2 |      1 |              1 |            2 |    1 |
|           1 |               1 |             3 |      1 |              1 |            2 |    1 |
|           1 |               1 |             3 |      2 |              2 |            3 |    2 |
|           1 |               1 |             4 |      1 |              1 |            2 |    1 |
|           1 |               1 |             4 |      2 |              2 |            3 |    2 |
|           1 |               1 |             4 |      3 |              3 |            4 |    3 |
|           1 |               2 |             1 |      1 |              2 |            1 |    1 |
|           2 |               2 |             3 |      2 |              2 |            3 |    1 |
|           2 |               2 |             4 |      2 |              2 |            3 |    1 |
|           2 |               2 |             4 |      3 |              3 |            4 |    2 |
|           2 |               3 |             1 |      2 |              3 |            2 |    1 |
|           2 |               3 |             1 |      1 |              2 |            1 |    2 |
|           2 |               3 |             2 |      2 |              3 |            2 |    1 |
|           3 |               3 |             4 |      3 |              3 |            4 |    1 |
|           3 |               4 |             1 |      3 |              4 |            3 |    1 |
|           3 |               4 |             1 |      2 |              3 |            2 |    2 |
|           3 |               4 |             1 |      1 |              2 |            1 |    3 |
|           3 |               4 |             2 |      3 |              4 |            3 |    1 |
|           3 |               4 |             2 |      2 |              3 |            2 |    2 |
|           3 |               4 |             3 |      3 |              4 |            3 |    1 |

1 Ответ

0 голосов
/ 28 марта 2020

Вы можете попробовать это

Create Table TripLocation
(TripID int,
 LocationID int,
 StopOrder int
 )

 Create table FromTo
(tripID int,
  fromLocationID int,
  fromStopOrder int,
  toLocationID int,
  toStopOrder int
  )


Create table cte1Temp
(startTripID int,
 startLocationID int,
 tripID int,
 fromLocationID int,
 toLocationID int,
 step int,
 path varchar(max)
) 


 --LIST OF LOCATIONS FOR EACH TRIP
 Insert into TripLocation
 Values
 (1,1,0),
 (1,2,1),
 (1,1,2),
 (2,2,0),
 (2,3,1),
 (2,2,2),
 (3,3,0),
 (3,4,1),
 (3,3,2)

 --LIST OF POSSIBLE TO/FROM COMBINATIONS FOR EACH TRIP BASED ON STOPORDER
  insert into FromTo
  select
FromLocation.tripID,
FromLocation.LocationID [fromLocationID],
FromLocation.StopOrder [fromStopOrder],
ToLocation.LocationID [toLocationID],
ToLocation.StopOrder [toStopOrder]
from
TripLocation FromLocation
join TripLocation ToLocation
  on FromLocation.tripID = ToLocation.tripID
  and ToLocation.StopOrder >= FromLocation.StopOrder
  and FromLocation.LocationID <> ToLocation.LocationID




;
--FIND ALL POSSIBLE END LOCATIONS FOR EACH START LOCATION IF TRIPS SHARE A COMMON LOCATION
  with cte1 as
   (
     select
     tripID [startTripID],
     fromLocationID [startLocationID],
     tripID,
     fromLocationID,
     toLocationID,
     1 [step],
     cast('1_' + ltrim(str(tripID)) + '-' + ltrim(str(tolocationID)) as varchar(max)) [path]
     from
     FromTo

     union all

     select
     anchor.startTripID,
     anchor.startLocationID,
     member.tripID,
     member.fromLocationID,
     member.toLocationID,
     anchor.step + 1 [step],
     anchor.path + ',' + ltrim(str(anchor.step + 1)) + '_' + + ltrim(str(member.tripID)) + '-' + ltrim(str(member.toLocationID)) 
     from
     FromTo member
     join cte1 anchor
       on anchor.toLocationID = member.fromLocationID
       and member.toLocationID <> anchor.fromLocationID
       and member.tripID <> anchor.tripID
    )


insert into cte1Temp
select
*
from
cte1

;

select 
  StartLocationID, 
  TolocationId,
  Substring(Value, 1,Charindex('-', Value)-1) as Trip, 
  Substring(Value, Charindex('-', Value)+1, LEN(Value)) as CrossingLocationID
from
cte1Temp
  CROSS APPLY STRING_SPLIT(path, ',')
order by StartLocationId, ToLocationId, Trip

Результаты:

StartLocationID TolocationId    Trip    CrossingLocationID
1   2   1_1 2
1   3   1_1 2
1   3   2_2 3
1   4   1_1 2
1   4   2_2 3
1   4   3_3 4
2   1   1_1 1
2   3   1_2 3
2   4   1_2 3
2   4   2_3 4
3   1   1_2 2
3   1   2_1 1
3   2   1_2 2
3   4   1_3 4
4   1   1_3 3
4   1   2_2 2
4   1   3_1 1
4   2   1_3 3
4   2   2_2 2
4   3   1_3 3
...