2012-11-15 14 views
9

Ho lottato con questo per tre giorni interi e non riesco a capirlo. È abbastanza simile a un post recente della mia "durata sequenziale t-sql", ma non esattamente la stessa ... Voglio reimpostare il numero di riga in base a una modifica nella colonna x (nel mio caso, colonna "who") ...T-sql Reimposta numero riga sul campo Modifica

Ecco la prima query che restituisce il un piccolo campione dei dati grezzi (ish):

SELECT  DISTINCT chr.custno, 
      CAST(LEFT(CONVERT(VARCHAR(20),chr.moddate,112),10)+ ' ' + chr.modtime AS DATETIME)as moddate, 
      chr.who  
FROM  <TABLE> chr 
WHERE  chr.custno = 581827 
      AND LEFT(chr.who, 5) = 'EMSZC' 
      AND chr.[description] NOT LIKE 'Recalled and viewed this customer' 
ORDER BY chr.custno 

Risultato:

custno  moddate    who 
581827  2012-11-08 08:38:00.000  EMSZC14 
581827  2012-11-08 08:41:10.000  EMSZC14 
581827  2012-11-08 08:53:46.000  EMSZC14 
581827  2012-11-08 08:57:04.000  EMSZC14 
581827  2012-11-08 08:58:35.000  EMSZC14 
581827  2012-11-08 08:59:13.000  EMSZC14 
581827  2012-11-08 09:00:06.000  EMSZC14 
581827  2012-11-08 09:04:39.000  EMSZC49 Reset row number to 1 
581827  2012-11-08 09:05:04.000  EMSZC49 
581827  2012-11-08 09:06:32.000  EMSZC49 
581827  2012-11-08 09:12:03.000  EMSZC49 
581827  2012-11-08 09:12:38.000  EMSZC49 
581827  2012-11-08 09:14:18.000  EMSZC49 
581827  2012-11-08 09:17:35.000  EMSZC14 Reset row number to 1 

secondo passo è quello di aggiungere il numero di riga (non ho fatto questo nella prima query a causa dell'uso della parola DISTINCT); così ...

WITH c1 AS (
     SELECT  DISTINCT chr.custno 
        CAST(LEFT(CONVERT(VARCHAR(20),chr.moddate,112),10)+ ' ' + chr.modtime AS DATETIME)as moddate, 
        chr.who 
     FROM  <TABLE> chr 
     WHERE  chr.custno = 581827 
        AND LEFT(chr.who, 5) = 'EMSZC' 
        AND chr.[description] NOT LIKE 'Recalled and viewed this customer' 
     ) 

SELECT ROW_NUMBER() OVER (PARTITION BY custno ORDER BY custno, moddate, who) AS RowID, custno, moddate, who 
FROM c1 

Risultato:

RowID custno  moddate      who 
1  581827  2012-11-08 08:38:00.000  EMSZC14 
2  581827  2012-11-08 08:41:10.000  EMSZC14 
3  581827  2012-11-08 08:53:46.000  EMSZC14 
4  581827  2012-11-08 08:57:04.000  EMSZC14 
5  581827  2012-11-08 08:58:35.000  EMSZC14 
6  581827  2012-11-08 08:59:13.000  EMSZC14 
7  581827  2012-11-08 09:00:06.000  EMSZC14 
8  581827  2012-11-08 09:04:39.000  EMSZC49 Reset row number to 1 
9  581827  2012-11-08 09:05:04.000  EMSZC49 
10  581827  2012-11-08 09:06:32.000  EMSZC49 
11  581827  2012-11-08 09:12:03.000  EMSZC49 
12  581827  2012-11-08 09:12:38.000  EMSZC49 
13  581827  2012-11-08 09:14:18.000  EMSZC49 
14  581827  2012-11-08 09:17:35.000  EMSZC14 Reset row number to 1 

Il passo successivo è dove mi sono bloccato: l'obiettivo è quello di ripristinare l'identificativo a 1 per ogni cambiamento di valore nella colonna “chi”. Il codice seguente ottiene un “quasi lì” risultato (e va notato che ho rubato/preso in prestito questo codice da qualche parte, ma ora non riesco a trovare il sito):

WITH c1 AS (
     SELECT  DISTINCT chr.custno, 
        CAST(LEFT(CONVERT(VARCHAR(20),chr.moddate,112),10)+ ' ' + chr.modtime AS DATETIME)as moddate, 
        chr.who 
     FROM  <TABLE> chr 
     WHERE  chr.custno = 581827 
        AND LEFT(chr.who, 5) = 'EMSZC' 
        AND chr.[description] NOT LIKE 'Recalled and viewed this customer' 
     ) 
, c1a AS (
      SELECT ROW_NUMBER() OVER (PARTITION BY custno ORDER BY custno, moddate, who) AS RowID, custno, moddate, who 
      FROM c1 
      ) 

SELECT x.RowID - y.MinID + 1 AS Row, 
     x.custno, x.Touch, x.moddate, x.who  
FROM (
      SELECT custno, who, MIN(RowID) AS MinID 
      FROM c1a 
      GROUP BY custno, who 
     ) AS y 
     INNER JOIN c1a x ON x.custno = y.custno AND x.who = y.who 

Risultato:

Row custno  moddate     who 
1 581827  2012-11-08 08:38:00.000  EMSZC14 
2 581827  2012-11-08 08:41:10.000  EMSZC14 
3 581827  2012-11-08 08:53:46.000  EMSZC14 
4 581827  2012-11-08 08:57:04.000  EMSZC14 
5 581827  2012-11-08 08:58:35.000  EMSZC14 
6 581827  2012-11-08 08:59:13.000  EMSZC14 
7 581827  2012-11-08 09:00:06.000  EMSZC14 
1 581827  2012-11-08 09:04:39.000  EMSZC49 Reset row number to 1 (Hooray! It worked!) 
2 581827  2012-11-08 09:05:04.000  EMSZC49 
3 581827  2012-11-08 09:06:32.000  EMSZC49 
4 581827  2012-11-08 09:12:03.000  EMSZC49 
5 581827  2012-11-08 09:12:38.000  EMSZC49 
6 581827  2012-11-08 09:14:18.000  EMSZC49 
14 581827  2012-11-08 09:17:35.000  EMSZC14 Reset row number to 1 (Crappies.) 

risultato desiderato:

Row custno  moddate      who 
1 581827  2012-11-08 08:38:00.000  EMSZC14 
2 581827  2012-11-08 08:41:10.000  EMSZC14 
3 581827  2012-11-08 08:53:46.000  EMSZC14 
4 581827  2012-11-08 08:57:04.000  EMSZC14 
5 581827  2012-11-08 08:58:35.000  EMSZC14 
6 581827  2012-11-08 08:59:13.000  EMSZC14 
7 581827  2012-11-08 09:00:06.000  EMSZC14 
1 581827  2012-11-08 09:04:39.000  EMSZC49 Reset row number to 1 
2 581827  2012-11-08 09:05:04.000  EMSZC49 
3 581827  2012-11-08 09:06:32.000  EMSZC49 
4 581827  2012-11-08 09:12:03.000  EMSZC49 
5 581827  2012-11-08 09:12:38.000  EMSZC49 
6 581827  2012-11-08 09:14:18.000  EMSZC49 
1 581827  2012-11-08 09:17:35.000  EMSZC14 Reset row number to 1 

Qualsiasi aiuto è apprezzato. Puoi ridacchiare anche a me, perché sono sicuro che questo è abbastanza semplice da risolvere - non riesco proprio a uscire dalla mia strada.

Grazie.

risposta

1

Invece di:

PARTITION BY custno ORDER BY custno, moddate, who) 

prova:

PARTITION BY custno, who ORDER BY custno, moddate) 
+0

Provato già (sembra che dovrebbe funzionare, non è vero?) Ma restituisce sempre lo stesso risultato impostato con l'ultimo numero di riga = 14. Continua a lanciare idee contro di me, però. Mi manca un semplice passaggio, ne sono sicuro. – Kris

-1

L'unica soluzione che viene in mente è quello di utilizzare un cursore (ugh) e soffrire per il processo rbar. NON una soluzione elegante in quanto il cursore dovrà leggere oltre 1 milione di righe. Bummer.

18

Se si utilizza SQL Server 2012, è possibile utilizzare LAG per confrontare il valore con la riga precedente ed è possibile utilizzare SUM e OVER per registrare le modifiche.

with C1 as 
(
    select custno, 
     moddate, 
     who, 
     lag(who) over(order by moddate) as lag_who 
    from chr 
), 
C2 as 
(
    select custno, 
     moddate, 
     who, 
     sum(case when who = lag_who then 0 else 1 end) 
      over(order by moddate rows unbounded preceding) as change 
    from C1 
) 
select row_number() over(partition by change order by moddate) as RowID, 
     custno, 
     moddate, 
     who 
from C2 

SQL Fiddle

Aggiornamento:

Una versione per SQL Server 2005. Si utilizza una CTE ricorsiva e una tabella temporanea per la memorizzazione intermedia dei dati è necessario per scorrere.

create table #tmp 
(
    id int primary key, 
    custno int not null, 
    moddate datetime not null, 
    who varchar(10) not null 
); 

insert into #tmp(id, custno, moddate, who) 
select row_number() over(order by moddate), 
     custno, 
     moddate, 
     who 
from chr; 

with C as 
(
    select 1 as rowid, 
     T.id, 
     T.custno, 
     T.moddate, 
     T.who, 
     cast(null as varchar(10)) as lag_who 
    from #tmp as T 
    where T.id = 1 
    union all 
    select case when T.who = C.who then C.rowid + 1 else 1 end, 
     T.id, 
     T.custno, 
     T.moddate, 
     T.who, 
     C.who 
    from #tmp as T 
    inner join C 
     on T.id = C.id + 1 
) 
select rowid, 
     custno, 
     moddate, 
     who 
from C 
option (maxrecursion 0); 

drop table #tmp; 

SQL Fiddle

+0

Sfortunatamente, stiamo ancora utilizzando il 2005 con l'intenzione di passare a 2008R2 il prossimo mese. La funzione LAG sarebbe una soluzione pulita a questo problema. Grazie per averlo portato alla mia attenzione ... solo un altro argomento per passare a SQL Server 2012. – Kris

+0

Soluzione molto interessante. La mia unica preoccupazione (e non è fondata di fatto) è l'uso di 0 con maxrecursion. Ciò potrebbe causare un ciclo infinito, ma nel pensare alla mia struttura dati, probabilmente non con i dati che ho. Grazie!!! – Kris

+0

@Kris Sei il benvenuto. –

4

ho avuto successo con questo problema utilizzando Rank():

SELECT RANK() OVER (PARTITION BY who ORDER BY custno, moddate) AS RANK 

Questo ha restituito i risultati desiderati. In realtà ho trovato questo post cercando di risolvere lo stesso problema.