2012-04-08 20 views
5

Bir dizi (IP varchar (15), DateTime datetime2) değerlerine sahip bir tablom var. Her satır, bir kullanıcı tarafından yapılan bir HTTP isteğine karşılık gelir. Bu satırlara oturum numaralarını atamak istiyorum. Farklı IP adreslerinin farklı oturum numaraları vardır. Son istek 30min'dan daha eskiyse aynı IP'ye numaralı yeni oturum numarası atanmalıdır.SQL Server: zamanaşımı tarafından bölümlenmiş row_number

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) 

Sütun 1 ve 2 girişleri, 3 ve 4, arzu edilen çıkışlar vardır: Burada örnek bir çıkıştır. Tablo iki kullanıcı gösterir.

Altta yatan tablo gerçekten büyük olduğundan, bu nasıl verimli şekilde çözülebilir? Verilerin (bir veya iki) üzerinde küçük bir miktar sabit geçiş yapmayı tercih ederim.

+0

Hangi SQL Server sürümü? 2012 ise yeni 'OVER' fıkra işlevselliği yardımcı olacaktır. –

+0

Evet, SQL Server 2012'dir. – usr

cevap

8

İşte birkaç denemede.

;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 

Bu/30 dakika (daha sonra IP, IPSessionNumber tarafından IP, DateTime tarafından) iki çeşit faaliyet göstermektedir fakat SessionNumber farklı benzersiz bir oturum numarası ip adresi başına her yeni oturuma atanır atanmaz keyfi sürece atanabilir farz etmez kural.

Kronolojik sırayla SessionNumber s öğesini atamak için. Aşağıdakileri kullandım.

;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 

Bu, sıralama işlemlerinin sayısını 4'e kadar artırır.

+0

İki IP'nin araya girmesi istendiğinde, oturumları karışmayacak mı? – Andomar

+0

@Andomar - İyi nokta! Sabit. –

+0

Pencere sayımı kullanmak zekice! Bu hileyi hatırlayacağım. – usr

2

Yinelemeli CTE'de kullanılabilecek bir kimlik oluşturmak için bir tablo değişkeni ve row_number kullanan bir sürüm. İmleci ve Martin (Martin tarafından sağlanan) bir sorguya karşı performansı karşılaştırmak faydalı olabilir.

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

Bu sürümü seviyorum, çünkü imleç tabanlı yaklaşım gibi, genişletmek kolaydır. Optimize edici bir sorunu gideren bir geçici tablo kullanmak için değiştirdim (tablo değişkenleri istatistik yok). Ayrıca, bu kodun çalıştığını doğruladım. Teşekkürler! – usr