Druhá odpověď je již pěkně dlouhá, takže ji nechávám tak, jak je. Tato odpověď je mnohem lepší, jednodušší a také správná, zatímco ta druhá má některé okrajové případy, které vedou k nesprávné odpovědi – toto cvičení nechám na čtenáři.
Poznámka:Pro přehlednost jsou přidány konce řádků. Celý blok je jeden dotaz
;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
Prvním krokem je nastavení "walker" rekurzivního tabulkového výrazu, který bude začínat v každé buňce a bude cestovat tak daleko, jak jen to půjde, aniž by musel opakovat jakýkoli krok. Ujištění, že buňky nejsou znovu navštěvovány, se provádí pomocí navštíveného sloupce, který ukládá každou buňku, která byla navštívena z každého počátečního bodu. Konkrétně tato podmínka AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
odmítne buňky, které již navštívil.
Abyste pochopili, jak zbytek funguje, musíte se podívat na výsledek generovaný CTE „Walker“ spuštěním „Select * from Walker order by StartX, StartY“ po CTE. "Kus" s 5 buňkami se objeví alespoň v 5 skupinách, každá s jiným (StartX,StartY)
, ale každá skupina má všech 5 (X,Y)
kusy s různými „navštívenými“ cestami.
Poddotaz (Z) používá LEFT JOIN + IS NULL k vyřazení skupin do jednoho řádku v každé skupině, která obsahuje "první souřadnici XY", definovanou podmínkou
Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
Záměrem je, aby každá buňka, kterou lze navštívit počínaje (StartX, StartY), porovnala mezi sebou buňku ve stejné skupině a našla buňku, kde ŽÁDNÁ OSTATNÍ buňka není na vyšším řádku, nebo pokud jsou na stejný řádek, je vlevo od této buňky. To nám však stále nechává příliš mnoho výsledků. Zvažte pouze 2-článkový kus na (3,4) a (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) ******
Zbývají 2 řádky s "první souřadnicí XY" z (3,4), označené ******
. Potřebujeme pouze jeden řádek, takže použijeme Row_Number, a protože číslováme, mohli bychom jít na nejdelší Visited
cesta, která by nám dala tolik buňky uvnitř kusu, jak můžeme získat.
Poslední vnější dotaz jednoduše vezme první řádky (RN=1) z každé podobné (X,Y) skupiny.
Chcete-li zobrazit VŠECHNY buňky každého kusu, změňte řádekselect X, Y, Visited
uprostřed do
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
Které poskytují tento výstup
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)