Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Come si identificano le sequenze di pattern di record nei record utilizzando TSQL?

Puoi utilizzare la seguente query racchiusa in un CTE per assegnare numeri di sequenza ai valori contenuti nella sequenza:

;WITH Seq AS (
    SELECT v, ROW_NUMBER() OVER(ORDER BY k) AS rn
    FROM (VALUES(1, 5), (2, 9), (3, 6)) x(k,v)
)

Risultato:

v   rn
-------
5   1
9   2
6   3

Utilizzando il CTE sopra si possono identificare le isole, cioè fette di righe sequenziali contenenti l'intera sequenza:

;WITH Seq AS (
    SELECT v, ROW_NUMBER() OVER(ORDER BY k) AS rn
    FROM (VALUES(1, 5), (2, 9), (3, 6)) x(k,v)
), Grp AS (
SELECT [Key], [Value], 
       ROW_NUMBER() OVER (ORDER BY [Key]) - rn AS grp            
FROM mytable AS m
LEFT JOIN Seq AS s ON m.Value = s.v
)
SELECT *
FROM Grp

Risultato:

    Key Value   grp
   -----------------
    1   5       0
    2   9       0
    3   6       0
    6   5       3
    7   9       3
    8   6       3

grp campo ti aiuta a identificare esattamente queste isole.

Tutto quello che devi fare ora è solo filtrare i gruppi parziali:

;WITH Seq AS (
    SELECT v, ROW_NUMBER() OVER(ORDER BY k) AS rn
    FROM (VALUES(1, 5), (2, 9), (3, 6)) x(k,v)
), Grp AS (
SELECT [Key], [Value], 
       ROW_NUMBER() OVER (ORDER BY [Key]) - rn AS grp            
FROM mytable AS m
LEFT JOIN Seq AS s ON m.Value = s.v
)
SELECT g1.[Key], g1.[Value]
FROM Grp AS g1
INNER JOIN (
   SELECT grp
   FROM Grp
   GROUP BY grp
   HAVING COUNT(*) = 3 ) AS g2
ON g1.grp = g2.grp

Dimostrazione qui

Nota: La versione iniziale di questa risposta utilizzava un INNER JOIN a Seq . Questo non funzionerà se la tabella contiene valori come 5, 42, 9, 6 , come 42 sarà filtrato dal INNER JOIN e questa sequenza falsamente identificata come valida. Il merito va a @HABO per questa modifica.