2008-09-17 16 views
24

Jaki jest najlepszy sposób obliczania pozycji percentyla (np. 90 percentyla lub mediana) w MSSQL 2005?Obliczanie rankingów percentyla w MS SQL

Chciałbym móc wybrać 25, medianę i 75 percentyle dla pojedynczej kolumny wyników (najlepiej w jednym rekordzie, dzięki czemu mogę łączyć ze średnią, maksymalną i minimalną). Tak na przykład, wyjście tabela wyników może być:

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

Odpowiedz

0

i pewnie korzystać z serwera SQL 2005

ROW_NUMBER() nad (kolejność wg oceny)/(select count (*) z ocen)

lub coś podobnego.

0

chciałbym zrobić coś takiego:

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 

jest to prawda?

14

ja że będzie to najprostsze rozwiązanie:

SELECT TOP N PERCENT FROM TheTable ORDER BY TheScore DESC 

Gdzie N = (100 - pożądane percentyl). Jeśli więc chcesz mieć wszystkie wiersze w 90. percentylu, wybierz 10% najlepszych.

Nie jestem pewien, co masz na myśli przez "najlepiej w jednym rekordzie". Czy masz na myśli obliczyć, jaki percentyl uzyskał dany wynik dla pojedynczego rekordu? na przykład Czy chcesz móc wydawać stwierdzenia typu "Twój wynik to 83, co stawia Cię w 91. percentylu". ?

EDYCJA: OK, pomyślałem trochę więcej o pańskim pytaniu i wymyśliłem tę interpretację. Czy pytasz, jak obliczyć punkt odcięcia dla danego percentyla? na przykład coś takiego: aby być w 90. percentylu musisz mieć wynik większy niż 78.

Jeśli tak, to zapytanie działa. Nie podoba mi się jednak pod-zapytań, więc w zależności od tego, do czego to było, prawdopodobnie spróbowałbym znaleźć bardziej eleganckie rozwiązanie. Zwraca jednak pojedynczy rekord z pojedynczym wynikiem.

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

pracuję na to trochę więcej, i oto co mam wymyślić do tej pory:

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 

Następnie w innej procedury przechowywanej zrobić to:

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 

Ciągle nie spełnia dokładnie tego, czego szukam. Otrzyma statystyki dla wszystkich testów; mając na uwadze, że chciałbym móc wybrać z tabeli TestScores, która ma wiele różnych testów i otrzymać te same statystyki dla każdego innego testu (jak mam w mojej przykładowej tabeli w moim pytaniu).

1

50 percentyl jest taki sam jak mediana. Podczas obliczania innego percentyla, powiedzmy 80, posortuj dane dla 80 procent danych w porządku rosnącym, a pozostałe 20 procent w porządku malejącym i weź średnią z dwóch środkowych wartości.

Uwaga: Zapytanie o medianę istnieje od dłuższego czasu, ale nie pamiętam, skąd dokładnie to zrobiłem, zmieniłem tylko to, aby obliczyć inne percentyle.

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

Sprawdź polecenie NTILE - da Ci centyle dość łatwo!

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

wyjątkiem ntile nie daje percentyle ... –

2

Jak o tym:

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