2008-09-17 4 views
24

Quelle est la meilleure façon de calculer les classements centiles (par exemple, le 90e centile ou le score médian) dans MSSQL 2005? J'aimerais pouvoir sélectionner le 25e, le médian et le 75e centile pour une seule colonne de scores (de préférence dans un seul enregistrement, donc je peux combiner avec la moyenne, la max. Et la min.). Ainsi, par exemple, la sortie de la table des résultats pourrait être:Calcul des classements centiles en MS SQL

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

Répondre

0

je serais probablement utiliser un serveur SQL 2005

ROW_NUMBER() sur (commande par score)/(select count (*) à partir des scores)

ou quelque chose le long de ces lignes.

0

je ferais quelque chose comme:

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 

est ce droit?

14

Je pense que ce serait la solution la plus simple:

SELECT TOP N PERCENT FROM TheTable ORDER BY TheScore DESC 

N = (100 - percentile désiré). Donc, si vous voulez toutes les lignes du 90e centile, vous devez sélectionner les 10% les plus élevés.

Je ne suis pas sûr de ce que vous entendez par "de préférence dans un seul enregistrement". Voulez-vous dire calculer quel percentile un score donné pour un seul enregistrement tomberait dans? par exemple. Voulez-vous être en mesure de faire des déclarations comme "votre score est de 83, ce qui vous place dans le 91e centile." ?

EDIT: OK, j'ai réfléchi un peu plus à votre question et j'ai trouvé cette interprétation. Demandez-vous comment calculer le score de coupure pour un percentile particulier? par exemple. quelque chose comme ceci: pour être dans le 90e percentile, vous devez avoir un score supérieur à 78.

Si oui, cette requête fonctionne. Cependant, je n'aime pas les sous-requêtes, donc en fonction de ce que ça voulait dire, j'essaierais probablement de trouver une solution plus élégante. Il renvoie cependant un seul enregistrement avec un seul score.

-- 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 
1

Je travaille sur ce un peu plus, et voici ce que je suis venu avec jusqu'à présent:

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 

Puis dans une autre procédure stockée je fais ceci:

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 

Il ne fait toujours pas ce que je cherche. Cela obtiendra les statistiques pour tous les tests; alors j'aimerais pouvoir sélectionner dans une table TestScores qui contient plusieurs tests différents et retrouver les mêmes statistiques pour chaque test différent (comme dans ma table d'exemple dans ma question).

1

Le 50e percentile est identique à la médiane. Lorsque vous calculez un autre centile, disons le 80e, trier les données pour les 80% des données dans l'ordre croissant et les 20% restants dans l'ordre décroissant, et prendre la moyenne des deux valeurs moyennes. NB: La requête médiane existe depuis longtemps, mais je ne me souviens plus exactement d'où je l'ai eu, je l'ai seulement modifiée pour calculer d'autres centiles.

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 
9

Vérifiez la commande NTILE - elle vous donnera des centiles assez facilement!

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) 
+4

Sauf NTILE ne donne pas centiles ... –

2

Que diriez-vous ceci:

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 
Questions connexes