2010-10-13 1 views
6

Ho bisogno di un approccio di classificazione T-SQL simile a quello fornito da NTILE(), tranne per il fatto che i membri di ciascuna tessera si troverebbero su una distribuzione scorrevole in modo che le tessere di livello più alto avere meno membriT-SQL: una migliore funzione di distribuzione scorrevole/query

Per esempio

CREATE TABLE #Rank_Table(
id int identity(1,1) not null, 
hits bigint not null default 0, 
PERCENTILE smallint null 
) 
--Slant the distribution of the data 
INSERT INTO #Rank_Table (hits) 
select CASE 
    when DATA > 9500 THEN DATA*30 
    WHEN data > 8000 THEN DATA*5 
    WHEN data < 7000 THEN DATA/3 +1 
    ELSE DATA 
END 
FROM 
(select top 10000 (ABS(CHECKSUM(NewId())) % 99 +1) * (ABS(CHECKSUM(NewId())) % 99 +1) DATA 
from master..spt_values t1 
    cross JOIN master..spt_values t2) exponential 

Declare @hitsPerGroup as bigint 
Declare @numGroups as smallint 
set @numGroups=100 

select @hitsPerGroup=SUM(hits)/(@numGroups -1) FROM #Rank_Table 

select @hitsPerGroup HITS_PER_GROUP 

--This is an even distribution 
SELECT id,HITS, NTILE(@numGroups) Over (Order By HITS DESC) PERCENTILE 
FROM #Rank_Table 
GROUP by id, HITS 

--This is my best attempt, but it skips groups because of the erratic distribution 
select 
    T1.ID, 
    T1.hits, 
    T.RunningTotal/@hitsPerGroup + 1 TILE, 
    T.RunningTotal 
FROM #Rank_Table T1 
     CROSS APPLY (Select SUM(hits) RunningTotal FROM #Rank_Table where hits <= T1.hits) T 
order by T1.hits 

DROP TABLE #Rank_Table 

In #Rank_table, NTILE (@numGroups) crea una distribuzione uniforme di gruppi @numGroups. Quello di cui ho bisogno sono i gruppi @numGroups in cui la tessera 1 ha il minor numero di membri, la tessera 2 ne avrà uno o più della tessera 1, la piastrella 3 avrà 1 o più della tessera 2 ... la tessera 100 ne avrebbe di più.

Utilizzo SQL Server 2008. In pratica, questo verrà eseguito su una tabella permanente con potenzialmente milioni di righe per aggiornare periodicamente la colonna PERCENTILE con il percentile da 1 a 100.

Il mio tentativo migliore sopra salterà i percentili e si comporta male. Ci deve essere un modo migliore.

+3

Le statistiche vengono utilizzate per descrivere un set di dati di grandi dimensioni in modo succinto che aiuta la comprensione. Dalle tue domande non è chiaro cosa stai cercando di fare o capire sul tuo set di dati. Le mediane, i percentili ecc sono ottimi per le normali distribuzioni e eliminano i valori anomali estremi con pochissime seccature. Sei sicuro di avere una distribuzione normale? Sembra più probabile che tu abbia una distribuzione esponenziale. Sarebbe più utile dire cosa stai cercando di capire sui tuoi dati piuttosto che chiedere informazioni sulle funzioni. –

risposta

0

Per creare una distribuzione più lineare, ho aggiunto una colonna calcolata alla tabella dati, HITS_SQRT HITS_SQRT AS (CONVERT([int],sqrt(HITS*4),(0))) PERSISTED.

Utilizzando questa colonna è possibile calcolare un numero target di "risultati per percentile".

select @hitsPerGroup=SUM(HITS_SQRT)/(@numGroups -1)[email protected], @dataPoints=COUNT(*) FROM #Rank_Table 

Lo script crea una tabella temporanea con un ROW_NUMBER() ordinati dal numero di colpi e itera le righe in ordine aggiornando il percentile da 100 a 1. descrescente Una totale parziale viene mantenuto il numero di colpi e quando viene passato il @hitsPerGroup, il percentile viene abbassato da 100 a 99, da 99 a 98, ecc.

Quindi la tabella dei dati di origine viene aggiornata con il suo percentile. C'è un indice della tabella di lavoro temporaneo per accelerare l'aggiornamento.

Script completo utilizzando #Rank_Table come tabella di dati di origine.

--Create Test Data 
CREATE TABLE #Rank_Table(
id int identity(1,1) not null, 
hits bigint not null default 0, 
PERCENTILE smallint NULL, 
HITS_SQRT AS (CONVERT([int],sqrt(HITS*4),(0))) PERSISTED 
) 
--Slant the distribution of the data 
INSERT INTO #Rank_Table (hits) 
select CASE 
    when DATA > 9500 THEN DATA*30 
    WHEN data > 8000 THEN DATA*5 
    WHEN data < 7000 THEN DATA/3 +1 
    ELSE DATA 
END 
FROM 
(select top 10000 (ABS(CHECKSUM(NewId())) % 99 +1) * (ABS(CHECKSUM(NewId())) % 99 +1) DATA 
from master..spt_values t1 
    cross JOIN master..spt_values t2) exponential 

--Create temp work table and variables to calculate percentiles 
    Declare @hitsPerGroup as int 
    Declare @numGroups as int 
    Declare @dataPoints as int 
    set @numGroups=100 

    select @hitsPerGroup=SUM(HITS_SQRT)/(@numGroups -1)[email protected], @dataPoints=COUNT(*) FROM #Rank_Table 

    --show the number of hits that each group should have 
    select @hitsPerGroup HITS_PER_GROUP 

    --Use temp table for the calculation 
    CREATE TABLE #tbl (
     row int, 
     hits int, 
     ID bigint, 
     PERCENTILE smallint null 
    ) 
    --add index to row 
    CREATE CLUSTERED INDEX idxRow ON #tbl(row) 

    insert INTO #tbl 
    select ROW_NUMBER() over (ORDER BY HITS), hits_SQRT, ID, null from #Rank_Table 

    --Update each row with a running total. 
    --lower the percentile by one when we cross a threshold for the maximum number of hits per group (@hitsPerGroup) 
    DECLARE @row as int 
    DEClare @runningTotal as int 
    declare @percentile int 
    set @row = 0 
    set @runningTotal = 0 
    set @percentile = @numGroups 

    while @row <= @dataPoints 
    BEGIN 
     select @[email protected] + hits from #tbl where [email protected] 

     if @runningTotal >= @hitsPerGroup 
     BEGIN 

      update #tbl 
      set [email protected] 
      WHERE PERCENTILE is null and row <@row 

      set @percentile = @percentile - 1 

      set @runningTotal = 0 
     END 

     --change rows 
     set @row = @row + 1 
    END 

    --get remaining 
    update #tbl 
    set [email protected] 
    WHERE PERCENTILE is null 

    --update source data 
    UPDATE m SET PERCENTILE = t.PERCENTILE 
    FROM #tbl t 
    inner join #Rank_Table m on t.ID=m.ID 


--Show the results 
    SELECT PERCENTILE, COUNT(id) NUMBER_RECORDS, SUM(HITS) HITS_IN_PERCENTILE 
    FROM #Rank_Table 
    GROUP BY PERCENTILE 
    ORDER BY PERCENTILE 

--cleanup 
    DROP TABLE #Rank_Table 
    DROP TABLE #tbl 

La prestazione non è stellare, ma raggiunge l'obiettivo di una distribuzione scorrevole uniforme.