Quelle est la meilleure façon de calculer les classements en centiles (par exemple, le 90e centile ou le score médian) dans MSSQL 2005?
J'aimerais pouvoir sélectionner le 25e, la médiane et le 75e centiles pour une seule colonne de scores (de préférence dans un seul enregistrement afin que je puisse combiner avec moyenne, max et min). Ainsi, par exemple, les résultats de la table pourraient être:
Group MinScore MaxScore AvgScore pct25 median pct75
----- -------- -------- -------- ----- ------ -----
T1 52 96 74 68 76 84
T2 48 98 74 68 75 85
Je penserais que ce serait la solution la plus simple:
SELECT TOP N PERCENT FROM TheTable ORDER BY TheScore DESC
Où N = (100 - centile souhaité). Donc, si vous voulez toutes les lignes du 90e centile, vous devez sélectionner les 10% supérieurs.
Je ne suis pas sûr de ce que vous entendez par "de préférence dans un seul enregistrement". Voulez-vous dire calculer dans quel centile se situerait un score donné pour un seul enregistrement? par exemple. voulez-vous pouvoir faire des déclarations telles que "votre score est de 83, ce qui vous place dans le 91ème centile". ?
EDIT: OK, j’ai réfléchi un peu plus à votre question et proposé cette interprétation. Demandez-vous comment calculer le seuil de coupure pour un centile particulier? par exemple. quelque chose comme ceci: pour être dans le 90ème centile, vous devez avoir un score supérieur à 78.
Si c'est le cas, cette requête fonctionne. Je n'aime pas les sous-requêtes, alors, selon le but recherché, j'essaierais probablement de trouver une solution plus élégante. Cependant, il ne renvoie qu'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
Consultez 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)
Que dis-tu de ça:
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
Le 50e centile est identique à la médiane. Lorsque vous calculez un autre centile, disons le 80e, triez les données pour 80% de données par ordre croissant et les 20% restantes par ordre décroissant, puis prenez la moyenne des deux valeurs centrales.
NB: La requête médiane existe depuis longtemps, mais je ne me souviens plus d'où elle provient exactement, je ne l'ai modifiée que 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
J'ai travaillé un peu plus là-dessus, et voici ce que j'ai inventé 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
Ensuite, 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 tout à fait ce que je cherche. Cela donnera les statistiques pour tous les tests; alors que j'aimerais pouvoir sélectionner un tableau TestScores contenant plusieurs tests différents et obtenir les mêmes statistiques pour chaque test différent (comme dans mon exemple de tableau de ma question).
j'utiliserais probablement un serveur SQL 2005
row_number () over (order by score)/(sélectionnez le compte (*) parmi les scores)
ou quelque chose du genre.
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 correct?