Det andet svar er allerede ret langt, så jeg lader det være som det er. Dette svar er meget bedre, enklere og også korrekt, mens det andet har nogle kant-cases, der vil give et forkert svar - den øvelse skal jeg overlade til læseren.
Bemærk:Linjeskift tilføjes for overskuelighed. Hele blokken er en enkelt forespørgsel
;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
Det første trin er at opsætte et "walker" rekursivt tabeludtryk, der starter ved hver celle og rejser så langt det kan uden at gå tilbage til noget trin. At sikre, at celler ikke besøges igen, sker ved at bruge den besøgte kolonne, som gemmer hver celle, der er blevet besøgt fra hvert udgangspunkt. Især denne betingelse AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
afviser celler, som den allerede har besøgt.
For at forstå, hvordan resten fungerer, skal du se på resultatet genereret af "Walker" CTE ved at køre "Select * from Walker order by StartX, StartY" efter CTE. Et "stykke" med 5 celler vises i mindst 5 grupper, hver med en forskellig (StartX,StartY)
, men hver gruppe har alle 5 (X,Y)
stykker med forskellige "besøgte" stier.
Underforespørgslen (Z) bruger en LEFT JOIN + IS NULL til at luge grupperne ned til den enkelte række i hver gruppe, der indeholder den "første XY-koordinat", defineret af betingelsen
Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
Hensigten er, at hver celle, der kan besøges fra (StartX, StartY), skal sammenlignes med hinandens celler i samme gruppe og finde den celle, hvor INGEN ANDEN celle er på en højere række, eller hvis de er på samme række, er til venstre for denne celle. Dette efterlader os dog stadig med for mange resultater. Overvej blot et 2-cellet stykke ved (3,4) og (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) ******
2 rækker tilbage med den "første XY-koordinat" af (3,4), markeret med ******
. Vi har kun brug for én række, så vi bruger Row_Number, og da vi nummererer, kan vi lige så godt gå efter den længste Visited
sti, som ville give os så mange af cellerne i stykket, som vi kan få.
Den sidste ydre forespørgsel tager simpelthen de første rækker (RN=1) fra hver lignende (X,Y) gruppe.
For at vise ALLE cellerne i hver brik skal du ændre linjenselect X, Y, Visited
i midten til
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
Som giver dette output
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)