it-swarm-eu.dev

Calcolo delle classifiche percentili in MS SQL

Qual è il modo migliore per calcolare le classifiche percentili (ad esempio il 90 ° percentile o il punteggio medio) in MSSQL 2005?

Mi piacerebbe essere in grado di selezionare il 25 °, il mediano e il 75 ° percentile per una singola colonna di punteggi (preferibilmente in un singolo record in modo da poter combinare con media, max e min). Ad esempio, l'output della tabella dei risultati potrebbe essere:

Group  MinScore  MaxScore  AvgScore  pct25  median  pct75
-----  --------  --------  --------  -----  ------  -----
T1     52        96        74        68     76      84
T2     48        98        74        68     75      85
25
Soldarnal

Penserei che questa sarebbe la soluzione più semplice:

SELECT TOP N PERCENT FROM TheTable ORDER BY TheScore DESC

Dove N = (100 - percentile desiderato). Quindi se volevi tutte le righe nel 90 ° percentile, dovresti selezionare il 10% più alto.

Non sono sicuro di cosa intendi con "preferibilmente in un singolo record". Intendi calcolare in quale percentile rientrerebbe un dato punteggio per un singolo record? per esempio. vuoi essere in grado di fare affermazioni come "il tuo punteggio è 83, che ti mette nel 91 ° percentile". ?

EDIT: OK, ho pensato un po 'di più alla tua domanda e ho trovato questa interpretazione. Stai chiedendo come calcolare il punteggio di taglio per un particolare percentile? per esempio. qualcosa del genere: per essere nel 90 ° percentile devi avere un punteggio maggiore di 78.

Se è così, questa query funziona. Non mi piacciono le sottoquery, quindi, a seconda di cosa fosse, probabilmente proverei a trovare una soluzione più elegante. Tuttavia, restituisce un singolo record con un singolo punteggio.

-- Find the minimum score for all scores in the 90th percentile
SELECT Min(subq.TheScore) FROM
(SELECT TOP 10 PERCENT TheScore FROM TheTable
ORDER BY TheScore DESC) AS subq
14
Matt

Controlla il comando NTILE: ti darà percentili abbastanza facilmente!

SELECT  SalesOrderID, 
    OrderQty,
    RowNum = Row_Number() OVER(Order By OrderQty),
    Rnk = RANK() OVER(ORDER BY OrderQty),
    DenseRnk = DENSE_RANK() OVER(ORDER BY OrderQty),
    NTile4  = NTILE(4) OVER(ORDER BY OrderQty)
FROM Sales.SalesOrderDetail 
WHERE SalesOrderID IN (43689, 63181)
9
Elizabeth

Cosa ne pensi di questo:

SELECT
  Group,
  75_percentile =  MAX(case when NTILE(4) OVER(ORDER BY score ASC) = 3 then score  else 0 end),
  90_percentile =  MAX(case when NTILE(10) OVER(ORDER BY score  ASC) = 9 then score  else 0 end)     
FROM TheScore
GROUP BY Group
2
Paul

Il 50 ° percentile è uguale alla mediana. Quando si calcola un altro percentile, ad esempio l'ottantesimo, ordinare i dati per l'80 percento dei dati in ordine ascendente e l'altro 20 percento in ordine discendente e prendere il valore medio del secondo valore.

NB: la query mediana è in circolazione da molto tempo, ma non ricordo da dove l'ho ottenuta, l'ho solo modificata per calcolare altri percentili.

DECLARE @Temp TABLE(Id INT IDENTITY(1,1), DATA DECIMAL(10,5))

INSERT INTO @Temp VALUES(0)
INSERT INTO @Temp VALUES(2)
INSERT INTO @Temp VALUES(8)
INSERT INTO @Temp VALUES(4)
INSERT INTO @Temp VALUES(3)
INSERT INTO @Temp VALUES(6)
INSERT INTO @Temp VALUES(6)
INSERT INTO @Temp VALUES(6) 
INSERT INTO @Temp VALUES(7)
INSERT INTO @Temp VALUES(0)
INSERT INTO @Temp VALUES(1)
INSERT INTO @Temp VALUES(NULL)


--50th percentile or median
SELECT ((
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 50 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA
                ) AS A
        ORDER BY DATA DESC) + 
        (
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 50 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA DESC
                ) AS A
        ORDER BY DATA ASC)) / 2.0


--90th percentile 
SELECT ((
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 90 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA
                ) AS A
        ORDER BY DATA DESC) + 
        (
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 10 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA DESC
                ) AS A
        ORDER BY DATA ASC)) / 2.0


--75th percentile
SELECT ((
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 75 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA
                ) AS A
        ORDER BY DATA DESC) + 
        (
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 25 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA DESC
                ) AS A
        ORDER BY DATA ASC)) / 2.0
1
Kay Aliu

Ho lavorato su questo un po 'di più, ed ecco cosa ho inventato finora:

CREATE PROCEDURE [dbo].[TestGetPercentile]

@percentile as float,
@resultval as float output

AS

BEGIN

WITH scores(score, prev_rank, curr_rank, next_rank) AS (
    SELECT dblScore,
        (ROW_NUMBER() OVER ( ORDER BY dblScore ) - 1.0) / ((SELECT COUNT(*) FROM TestScores) + 1)  [prev_rank],
        (ROW_NUMBER() OVER ( ORDER BY dblScore ) + 0.0) / ((SELECT COUNT(*) FROM TestScores) + 1)  [curr_rank],
        (ROW_NUMBER() OVER ( ORDER BY dblScore ) + 1.0) / ((SELECT COUNT(*) FROM TestScores) + 1)  [next_rank]
    FROM TestScores
)

SELECT @resultval = (
    SELECT TOP 1 
    CASE WHEN t1.score = t2.score
        THEN t1.score
    ELSE
        t1.score + (t2.score - t1.score) * ((@percentile - t1.curr_rank) / (t2.curr_rank - t1.curr_rank))
    END
    FROM scores t1, scores t2
    WHERE (t1.curr_rank = @percentile OR (t1.curr_rank < @percentile AND t1.next_rank > @percentile))
        AND (t2.curr_rank = @percentile OR (t2.curr_rank > @percentile AND t2.prev_rank < @percentile))
)

END

Quindi in un'altra stored procedure lo faccio:

DECLARE @pct25 float;
DECLARE @pct50 float;
DECLARE @pct75 float;

exec SurveyGetPercentile .25, @pct25 output
exec SurveyGetPercentile .50, @pct50 output
exec SurveyGetPercentile .75, @pct75 output

Select
    min(dblScore) as minScore,
    max(dblScore) as maxScore,
    avg(dblScore) as avgScore,
    @pct25 as percentile25,
    @pct50 as percentile50,
    @pct75 as percentile75
From TestScores

Ancora non fa esattamente quello che sto cercando. Questo otterrà le statistiche per tutti i test; mentre mi piacerebbe essere in grado di selezionare da una tabella TestScores che ha più test differenti in esso e recuperare le stesse statistiche per ogni test diverso (come ho nella mia tabella di esempio nella mia domanda).

1
Soldarnal

probabilmente userò un server sql 2005 

row_number () over (ordine per punteggio)/(seleziona count (*) tra i punteggi)

o qualcosa del genere. 

0
karl prosser

farei qualcosa come:

select @n = count(*) from tbl1
select @median = @n / 2
select @p75 = @n * 3 / 4
select @p90 = @n * 9 / 10

select top 1 score from (select top @median score from tbl1 order by score asc) order by score desc

è giusto?

0
syap