2012-11-15 8 views
9

Walczyłem z tym od trzech pełnych dni i nie mogę się z tym pogodzić. Jest to bardzo podobne do ostatniego postu "t-sql sequential duration", ale nie do końca tego samego ... Chcę zresetować numer wiersza na podstawie zmiany w kolumnie x (w moim przypadku kolumna "kto") ...T-sql Zresetuj numer wiersza na zmianie pola

Oto pierwsze zapytanie, które zwraca niewielką próbkę nierozcieńczonych (ish) dane:

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 

Wynik:

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 

Drugim krokiem jest dodanie numer wiersza (I nie zrobił to w pierwszym zapytaniu z powodu użycia słowa DISTINCT); tak ...

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 

Wynik:

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 

Następnym krokiem jest gdzie utknąłem: celem jest, aby zresetować rowid do 1 na każdej zmianie wartości w kolumnie „kto”. Poniższy kod pobiera „Almost There” wynik (i należy zauważyć, że ukradł/pożyczył ten kod gdzieś, ale teraz nie mogę znaleźć na stronie internetowej):

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 

Wynik:

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.) 

pożądany rezultat:

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 

Każda pomoc jest mile widziana. Możesz się na mnie śmiać, ponieważ jestem pewien, że jest to łatwe do rozwiązania - po prostu nie mogę wyjść z mojej własnej.

Dzięki.

Odpowiedz

1

Zamiast:

PARTITION BY custno ORDER BY custno, moddate, who) 

try:

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

Próbowałem już (wygląda na to, że powinien zadziałać, prawda?), Ale wciąż zwraca ten sam zestaw wyników z ostatnim numerem wiersza = 14. Nie przestawaj mi jednak wyrzucać pomysłów. Po prostu brakuje mi jakiegoś prostego kroku. – Kris

-1

Jedynym rozwiązaniem można myślę, jest użycie kursora (ugh) i cierpią przez proces RBAR. NIE jest to eleganckie rozwiązanie, ponieważ kursor będzie musiał odczytać ponad 1m wierszy. Bummer.

18

Jeśli korzystasz z programu SQL Server 2012, możesz użyć wartości LAG w celu porównania wartości z poprzednim wierszem. Do zapisania zmian można użyć wartości SUM i OVER.

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

Aktualizacja:

Wersja dla SQL Server 2005. Wykorzystuje rekurencyjnej CTE oraz tabelę temp dla pośredniczącego przechowywania danych potrzebnych do iteracyjnego.

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

Niestety, nadal pracujemy w 2005 r. Z planami aktualizacji do 2008R2 w przyszłym miesiącu. Funkcja LGD byłaby czystym rozwiązaniem tego problemu. Dziękuję za zwrócenie mi uwagi ... kolejny argument przemawiający za przejściem na SQL Server 2012. – Kris

+0

Bardzo fajne rozwiązanie. Moją jedyną obawą (i nie jest to uzasadnione) jest użycie 0 z maxrecursion. To może spowodować nieskończoną pętlę, ale w myśleniu o mojej strukturze danych, prawdopodobnie nie z danymi, które mam. Dziękuję Ci!!! – Kris

+0

@ Kris Nie ma za co. –

4

miałem sukces z tym problemem za pomocą Rank():

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

ten powrócił pożądanych rezultatów. Faktycznie znalazłem ten post, próbując rozwiązać ten sam problem.