Câu trả lời khác đã khá dài, vì vậy tôi sẽ để nguyên nó. Câu trả lời này tốt hơn, đơn giản hơn và cũng chính xác trong khi câu kia có một số trường hợp phức tạp sẽ tạo ra một câu trả lời sai - Tôi sẽ để bài tập đó cho người đọc.
Lưu ý:Các dấu ngắt dòng được thêm vào để rõ ràng. Toàn bộ khối là một truy vấn duy nhất
;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
Bước đầu tiên là thiết lập một biểu thức bảng đệ quy "walker" sẽ bắt đầu ở mỗi ô và di chuyển xa nhất có thể mà không cần rút lại bất kỳ bước nào. Đảm bảo rằng các ô không được truy cập lại được thực hiện bằng cách sử dụng cột đã truy cập, cột này lưu trữ từng ô đã được truy cập từ mọi điểm bắt đầu. Đặc biệt, điều kiện này AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
từ chối các ô mà nó đã truy cập.
Để hiểu cách hoạt động của phần còn lại, bạn cần xem kết quả được tạo bởi CTE "Walker" bằng cách chạy "Chọn * từ thứ tự Walker theo StartX, StartY" sau CTE. Một "mảnh" có 5 ô xuất hiện trong ít nhất 5 nhóm, mỗi nhóm có một (StartX,StartY)
khác nhau , nhưng mỗi nhóm có tất cả 5 (X,Y)
các phần có đường dẫn "Đã truy cập" khác nhau.
Truy vấn con (Z) sử dụng LEFT JOIN + IS NULL để xóa các nhóm xuống hàng đơn trong mỗi nhóm có chứa "tọa độ XY đầu tiên", được xác định bởi điều kiện
Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
Mục đích là cho mỗi ô có thể được truy cập bắt đầu từ (StartX, StartY), để so sánh với các ô khác trong cùng một nhóm và để tìm ô mà KHÔNG có ô KHÁC nằm trên hàng cao hơn hoặc nếu chúng nằm trên cùng hàng, ở bên trái của ô này. Tuy nhiên, điều này vẫn để lại cho chúng ta quá nhiều kết quả. Chỉ xem xét một mảnh 2 ô tại (3,4) và (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 hàng vẫn có "tọa độ XY đầu tiên" của (3,4), được đánh dấu bằng ******
. Chúng tôi chỉ cần một hàng, vì vậy chúng tôi sử dụng Row_Number và vì chúng tôi đang đánh số, chúng tôi cũng có thể sử dụng Visited
dài nhất đường dẫn này sẽ cung cấp cho chúng ta càng nhiều các ô trong mảnh mà chúng ta có thể lấy được.
Truy vấn bên ngoài cuối cùng chỉ cần lấy các hàng đầu tiên (RN =1) từ mỗi nhóm (X, Y) tương tự.
Để hiển thị TẤT CẢ các ô của mỗi phần, hãy thay đổi dòngselect X, Y, Visited
ở giữa đến
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
Cái nào đưa ra kết quả này
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)