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
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
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)
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
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
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).
probabilmente userò un server sql 2005
row_number () over (ordine per punteggio)/(seleziona count (*) tra i punteggi)
o qualcosa del genere.
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?