2013-01-02 6 views
18

Ho una tabella con una colonna ID e un'altra colonna con un numero. Un ID può avere più numeri. Per esempioUnione di dati in una singola tabella SQL senza un cursore

ID | Number 
1 | 25 
1 | 26 
1 | 30 
1 | 24 
2 | 4 
2 | 8 
2 | 5 

ora con sede di questi dati, in una nuova tabella, voglio avere questo

ID | Low | High 
1 | 24 | 26 
1 | 30 | 30 
2 | 4 | 5 
2 | 8 | 8 

Come potete vedere, voglio unire i dati dove i numeri consecutivi, come 24, 25, 26. Quindi ora il minimo era 24, il massimo era 26, e quindi il 30 è ancora un intervallo separato. Ho a che fare con grandi quantità di dati, quindi preferirei non usare un cursore per il bene delle prestazioni (che è quello che stavo facendo in precedenza, e stavo rallentando un po 'le cose) ... Qual è il modo migliore per ottenere questo ? Non sono un professionista SQL, quindi non sono sicuro che sia disponibile una funzione che potrebbe renderlo più semplice, o quale sarebbe il modo più veloce per farlo.

Grazie per l'aiuto.

+3

quello RDBMS stai usando? – Taryn

+0

Spiacente, ho aggiunto un altro tag alla domanda. È SQL Server 2008. –

+0

Quasi lo stesso: [query SQL per raggruppare i risultati in base alla sequenza] (http://stackoverflow.com/questions/5087864/sql-query-for-grouping-the-results-based-on -sequenza). –

risposta

36

L'osservazione chiave è che una sequenza di numeri meno un'altra sequenza è una costante. Possiamo generare un'altra sequenza usando row_number. Questo identifica tutti i gruppi:

select id, MIN(number) as low, MAX(number) as high 
from (select t.*, 
      (number - ROW_NUMBER() over (partition by id order by number)) as groupnum 
     from t 
    ) t 
group by id, groupnum 

Il resto è solo aggregazione.

0

Suggerirei di utilizzare una struttura di loop WHILE con una variabile di tabella anziché il cursore,.

Per esempio,

DECLARE @TableVariable TABLE 
(
    MyID int IDENTITY (1, 1) PRIMARY KEY NOT NULL, 
    [ID] int, 
    [Number] int 
) 

DECLARE @Count int, @Max int 

INSERT INTO @TableVariable (ID, Number) 
SELECT ID, Number 
FROM YourSourceTable 

SELECT @Count = 1, @Max = MAX(MyID) 
FROM @TableVariable 

WHILE @Count <= @Max 
BEGIN 

    ...do your processing here... 


    SET @Count = @Count + 1 

END 
+0

Cosa? No; i cicli in SQL sono quasi sempre la cosa sbagliata da fare. SQL è stato progettato tenendo in considerazione la logica dei set e le implementazioni di solito sono le più performanti quando vengono scritte per trarne vantaggio. Ho anche la sensazione che questo tipo di ciclo "WHILE" userà alcuni degli stessi processi sottostanti di un cursore, il che ti lascerebbe in un punto altrettanto brutto di prima. Oltre a ciò, non hai mostrato le condizioni necessarie per reimpostare '@ Count' ... –

+1

In combinazione con una variabile table ci sono stati molti casi in cui i guadagni in termini di prestazioni sono valsi la pena. '@ Count' in questo caso non ha bisogno di essere resettato, appena incrementato man mano che ogni record nella variabile di tabella viene elaborato, anche se ciò potrebbe cambiare in base ai requisiti dell'OP. –

3

Soluzione con CTE e ricorsione:

WITH CTE AS (
    SELECT T.ID, T.NUMBER, T.NUMBER AS GRP 
    FROM T 
    LEFT OUTER JOIN T T2 ON T.ID = T2.ID AND T.NUMBER -1 = T2.NUMBER 
    WHERE T2.ID IS NULL 
    UNION ALL 
    SELECT T.ID, T.NUMBER, GRP 
    FROM CTE 
    INNER JOIN T 
    ON T.ID = CTE.ID AND T.NUMBER = CTE.NUMBER + 1 
) 
SELECT ID, MAX(NUMBER), MIN(NUMBER) 
FROM CTE 
GROUP BY ID, GRP 

Results at fiddlesql

Problemi correlati