sql >> Database >  >> RDS >> Sqlserver

Bekijk om gegroepeerde waarden of objecten te identificeren

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 regel
select 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)


  1. SQL Server CTE en recursie voorbeeld

  2. Hoe het gemiddelde van de 'middelste' waarden in een groep te krijgen?

  3. Django 1.8 Migratie met Postgres BDR 9.4.1

  4. Retourneer eerste x resultaten van elke groep in SQL-query