Het andere antwoord is al behoorlijk lang, dus ik laat het zoals het is. Dit antwoord is veel beter, eenvoudiger en ook correct, terwijl het andere een aantal randgevallen heeft die een verkeerd antwoord zullen opleveren - die oefening laat ik aan de lezer over.
Opmerking:voor de duidelijkheid zijn regeleinden toegevoegd. Het hele blok is een enkele vraag
;with Walker(StartX,StartY,X,Y,Visited) as (
select X,Y,X,Y,CAST('('+right(X,3)+','+right(Y,3)+')' as Varchar(Max))
from puzzle
union all
select W.StartX,W.StartY,P.X,P.Y,W.Visited+'('+right(P.X,3)+','+right(P.Y,3)+')'
from Walker W
join Puzzle P on
(W.X=P.X and W.Y=P.Y+1 OR -- these four lines "collect" a cell next to
W.X=P.X and W.Y=P.Y-1 OR -- the current one in any direction
W.X=P.X+1 and W.Y=P.Y OR
W.X=P.X-1 and W.Y=P.Y)
AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
)
select X, Y, Visited
from
(
select W.X, W.Y, W.Visited, rn=row_number() over (
partition by W.X,W.Y
order by len(W.Visited) desc)
from Walker W
left join Walker Other
on Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
where Other.X is null
) Z
where rn=1
De eerste stap is het opzetten van een recursieve tabeluitdrukking "walker" die bij elke cel zal beginnen en zo ver mogelijk zal reizen zonder enige stap terug te gaan. Ervoor zorgen dat cellen niet opnieuw worden bezocht, wordt gedaan door de bezochte kolom te gebruiken, waarin elke cel wordt opgeslagen die vanaf elk startpunt is bezocht. In het bijzonder geldt deze voorwaarde AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
weigert cellen die het al heeft bezocht.
Om te begrijpen hoe de rest werkt, moet u kijken naar het resultaat dat is gegenereerd door de "Walker" CTE door "Select * from Walker order by StartX, StartY" na de CTE uit te voeren. Een "stukje" met 5 cellen verschijnt in minstens 5 groepen, elk met een andere (StartX,StartY)
, maar elke groep heeft alle 5 (X,Y)
stukken met verschillende "Bezochte" paden.
De subquery (Z) gebruikt een LEFT JOIN + IS NULL om de groepen te verwijderen tot de enkele rij in elke groep die de "eerste XY-coördinaat" bevat, gedefinieerd door de voorwaarde
Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
Het is de bedoeling dat elke cel die bezocht kan worden vanaf (StartX, StartY), met elkaar vergeleken wordt in dezelfde groep, en om de cel te vinden waar GEEN ANDERE cel op een hogere rij staat, of als ze op de dezelfde rij, bevindt zich links van deze cel. Dit laat ons echter nog steeds met te veel resultaten. Beschouw gewoon een 2-cellig stuk bij (3,4) en (4,4):
StartX StartY X Y Visited
3 4 3 4 (3,4) ******
3 4 4 4 (3,4)(4,4)
4 4 4 4 (4,4)
4 4 3 4 (4,4)(3,4) ******
Er blijven 2 rijen over met de "eerste XY-coördinaat" van (3,4), gemarkeerd met ******
. We hebben maar één rij nodig, dus we gebruiken Row_Number en aangezien we nummeren, kunnen we net zo goed gaan voor de langste Visited
pad, wat ons zoveel . zou opleveren de cellen binnen het stuk zoals we kunnen krijgen.
De laatste buitenste query neemt gewoon de eerste rijen (RN=1) van elke vergelijkbare (X,Y) groep.
Om ALLE cellen van elk stuk te tonen, verander de regelselect X, Y, Visited
in het midden naar
select X, Y, (
select distinct '('+right(StartX,3)+','+right(StartY,3)+')'
from Walker
where X=Z.X and Y=Z.Y
for xml path('')
) PieceCells
Welke deze output geven
X Y PieceCells
1 1 (1,1)(2,1)(2,2)(3,2)
3 4 (3,4)(4,4)
5 6 (5,6)
7 5 (7,5)(8,5)(9,5)
8 1 (10,1)(8,1)(8,2)(9,1)(9,2)(9,3)