2012-04-08 41 views
5

Mam tabelę z serią wartości (IP varchar (15), DateTime datetime2). Każdy wiersz odpowiada żądaniu HTTP zgłoszonemu przez użytkownika. Chcę przypisać numery sesji do tych wierszy. Różne adresy IP mają różne numery sesji. Ten sam adres IP powinien mieć przypisany nowy numer sesji , jeśli ostatnie żądanie jest starsze niż 30 minut. Oto przykładowe wyjście:Serwer SQL: numer_wiersza podzielony przez timeout

IP,  DateTime,   SessionNumber, RequestNumber 
1.1.1.1, 2012-01-01 00:01, 1,    1 
1.1.1.1, 2012-01-01 00:02, 1,    2 
1.1.1.1, 2012-01-01 00:03, 1,    3 
1.1.1.2, 2012-01-01 00:04, 2,    1 --different IP => new session number 
1.1.1.2, 2012-01-01 00:05, 2,    2 
1.1.1.2, 2012-01-01 00:40, 3,    1 --same IP, but last request 35min ago (> 30min) 

Kolumny 1 i 2 to wejścia, 3 i 4 to żądane wyjścia. Tabela pokazuje dwóch użytkowników.

Podstawową tabelą jest naprawdę duża, jak można to skutecznie rozwiązać? Wolałbym małą stałą liczbę przejść przez dane (jedną lub dwie).

+0

Jaką wersję programu SQL Server? W 2012 roku pomocna będzie nowa funkcja klauzula "OVER". –

+0

Tak, to jest SQL Server 2012. – usr

Odpowiedz

8

Oto kilka prób.

;WITH CTE1 AS 
(
SELECT *, 
IIF(DATEDIFF(MINUTE, 
     LAG(DateTime) OVER (PARTITION BY IP ORDER BY DateTime), 
     DateTime) < 30,0,1) AS SessionFlag 
FROM Sessions 
), CTE2 AS 
(
SELECT *, 
     SUM(SessionFlag) OVER (PARTITION BY IP 
            ORDER BY DateTime) AS IPSessionNumber 
FROM CTE1 
) 
SELECT IP, 
     DateTime, 
     DENSE_RANK() OVER (ORDER BY IP, IPSessionNumber) AS SessionNumber, 
     ROW_NUMBER() OVER (PARTITION BY IP, IPSessionNumber 
           ORDER BY DateTime) AS RequestNumber 
FROM CTE2 

ten ma dwie operacje sortowania (według IP, DateTime następnie przez IP, IPSessionNumber), ale nie zakładamy, że SessionNumber można przypisać dowolnie, o ile inny unikalny numer sesji jest przypisany do każdej nowej sesji za adres IP/30 minut reguła.

Aby przyporządkować kolejno SessionNumber w porządku chronologicznym. Użyłem następujących.

;WITH CTE1 AS 
(
SELECT *, 
IIF(DATEDIFF(MINUTE, 
     LAG(DateTime) OVER (PARTITION BY IP ORDER BY DateTime), 
     DateTime) < 30,0,1) AS SessionFlag 
FROM Sessions 
), CTE2 AS(
SELECT *, 
     SUM(SessionFlag) OVER (ORDER BY DateTime) AS GlobalSessionNo 
FROM CTE1 
), CTE3 AS(
SELECT *, 
     MAX(CASE WHEN SessionFlag = 1 THEN GlobalSessionNo END) 
       OVER (PARTITION BY IP ORDER BY DateTime) AS SessionNumber 
FROM CTE2) 
SELECT IP, 
     DateTime, 
     SessionNumber, 
     ROW_NUMBER() OVER (PARTITION BY SessionNumber 
           ORDER BY DateTime) AS RequestNumber 
FROM CTE3 

Zwiększa to liczbę operacji sortowania do 4 jednak.

+0

Jeśli żądania z dwóch przeplatanych adresów IP, czy ich sesje nie będą się mieszać? – Andomar

+0

@Andomar - Dobra uwaga! Naprawiony. –

+0

Używanie liczby okien jest genialne! Zapamiętam tę sztuczkę. – usr

2

Oto wersja wykorzystująca zmienną tabeli i numer_wiersza do utworzenia identyfikatora, który może być użyty w rekurencyjnym CTE. Warto porównać wydajność z kursorem i jedną wersją (dostarczoną przez Martina).

CREATE TABLE #T 
(
    IP varchar(15), 
    DateTime datetime, 
    ID int, 
    primary key (IP, ID) 
) 

insert into #T(IP, DateTime, ID) 
select IP, DateTime, row_number() over(partition by IP order by DateTime) 
from #sessionRequests 

;with C as 
(
    select IP, 
     ID, 
     DateTime, 
     1 as Session 
    from #T 
    where ID = 1 
    union all 
    select T.IP, 
     T.ID, 
     T.DateTime, 
     C.Session + case when datediff(minute, C.DateTime, T.DateTime) >= 30 then 1 else 0 end 
    from #T as T 
    inner join C 
     on T.IP = C.IP and 
     T.ID = C.ID + 1 
) 
SELECT IP, 
     DateTime, 
     dense_rank() over(order by IP, Session) as SessionNumber, 
     row_number() over(partition by IP, Session order by DateTime) as RequestNumber 
from C 
order by IP, DateTime, SessionNumber, RequestNumber 
option (maxrecursion 0) 
+1

Podoba mi się ta wersja, ponieważ jest łatwa do rozszerzenia, prawie jak podejście oparte na kursorach. Zmieniłem go, aby użyć tabeli temp, która naprawiła problem z optymalizatorem (zmienne tabeli nie mają statystyk). Ponadto zweryfikowałem, że ten kod działa. Dzięki! – usr