sql >> Databasteknik >  >> RDS >> Sqlserver

Visa för att identifiera grupperade värden eller objekt

Det andra svaret är redan ganska långt, så jag lämnar det som det är. Det här svaret är mycket bättre, enklare och även korrekt, medan det andra har några kantfall som kommer att ge ett felaktigt svar - jag lämnar den övningen till läsaren.

Obs! Radbrytningar läggs till för tydlighetens skull. Hela blocket är en enda fråga

;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örsta steget är att sätta upp ett "walker" rekursivt tabelluttryck som börjar vid varje cell och färdas så långt det kan utan att gå tillbaka till något steg. Att se till att celler inte återbesöks görs genom att använda den besökta kolumnen, som lagrar varje cell som har besökts från varje startpunkt. I synnerhet detta villkor AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%' avvisar celler som den redan har besökt.

För att förstå hur resten fungerar måste du titta på resultatet som genereras av "Walker" CTE genom att köra "Select * from Walker order by StartX, StartY" efter CTE. En "bit" med 5 celler visas i minst 5 grupper, var och en med olika (StartX,StartY) , men varje grupp har alla 5 (X,Y) bitar med olika "besökta" vägar.

Underfrågan (Z) använder en LEFT JOIN + IS NULL för att rensa ner grupperna till den enda raden i varje grupp som innehåller den "första XY-koordinaten", definierad av villkoret

     Other.StartX=W.StartX and Other.StartY=W.StartY
        and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))

Avsikten är att varje cell som kan besökas med början från (StartX, StartY), ska jämföra mot varandra i samma grupp och hitta cellen där INGEN ANNAN cell är på en högre rad, eller om de finns på samma rad, är till vänster om denna cell. Detta lämnar oss fortfarande med för många resultat. Tänk bara på en 2-cellsbit vid (3,4) och (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 rader kvarstår med den "första XY-koordinaten" av (3,4), markerade med ****** . Vi behöver bara en rad, så vi använder Row_Number och eftersom vi numrerar kan vi lika gärna välja den längsta Visited sökväg, vilket skulle ge oss så många av cellerna i stycket som vi kan få.

Den sista yttre frågan tar helt enkelt de första raderna (RN=1) från varje liknande (X,Y) grupp.

För att visa ALLA celler i varje del, ändra raden
select X, Y, Visited

i mitten till

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 ger denna utdata

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)


  1. PHP-sidan visar råkod

  2. Vad händer om jag behöver ett väldigt stort autoinkrement-ID?

  3. Fråga prestandaoptimering i MySQL

  4. Olaglig blandning av sammanställningar (utf8mb4_unicode_ci,IMPLICIT) och (utf8mb4_general_ci,IMPLICIT) för operation '='