2010-10-13 19 views
6

Tôi cần một phương pháp xếp hạng T-SQL tương tự như phương thức NTILE cung cấp(), ngoại trừ các thành viên của mỗi ô sẽ có phân phối trượt sao cho xếp hạng cao hơn có ít thành viên hơn.T-SQL: Một chức năng phân phối/truy vấn trượt tốt hơn

Ví dụ

CREATE TABLE #Rank_Table(
id int identity(1,1) not null, 
hits bigint not null default 0, 
PERCENTILE smallint null 
) 
--Slant the distribution of the data 
INSERT INTO #Rank_Table (hits) 
select CASE 
    when DATA > 9500 THEN DATA*30 
    WHEN data > 8000 THEN DATA*5 
    WHEN data < 7000 THEN DATA/3 +1 
    ELSE DATA 
END 
FROM 
(select top 10000 (ABS(CHECKSUM(NewId())) % 99 +1) * (ABS(CHECKSUM(NewId())) % 99 +1) DATA 
from master..spt_values t1 
    cross JOIN master..spt_values t2) exponential 

Declare @hitsPerGroup as bigint 
Declare @numGroups as smallint 
set @numGroups=100 

select @hitsPerGroup=SUM(hits)/(@numGroups -1) FROM #Rank_Table 

select @hitsPerGroup HITS_PER_GROUP 

--This is an even distribution 
SELECT id,HITS, NTILE(@numGroups) Over (Order By HITS DESC) PERCENTILE 
FROM #Rank_Table 
GROUP by id, HITS 

--This is my best attempt, but it skips groups because of the erratic distribution 
select 
    T1.ID, 
    T1.hits, 
    T.RunningTotal/@hitsPerGroup + 1 TILE, 
    T.RunningTotal 
FROM #Rank_Table T1 
     CROSS APPLY (Select SUM(hits) RunningTotal FROM #Rank_Table where hits <= T1.hits) T 
order by T1.hits 

DROP TABLE #Rank_Table 

Trong #Rank_table, NTILE (@numGroups) tạo ra một phân bố của các nhóm @numGroups. Những gì tôi cần là các nhóm @numGroups nơi các tile 1 có các thành viên ít nhất, tile 2 sẽ có một hoặc nhiều hơn 1 tile, tile 3 sẽ có 1 hoặc nhiều hơn tile 2 ... tile 100 sẽ có nhiều nhất.

Tôi đang sử dụng SQL Server 2008. Trong thực tế, thao tác này sẽ được chạy trên bảng vĩnh viễn với hàng triệu hàng có thể để cập nhật định kỳ cột PERCENTILE với phần trăm từ 1-100.

Nỗ lực tốt nhất của tôi ở trên sẽ bỏ qua phần trăm và hoạt động kém. Phải có cách tốt hơn.

+3

Thống kê được sử dụng để mô tả một tập dữ liệu lớn theo cách gọn gàng nhằm hỗ trợ sự hiểu biết. Nó không rõ ràng từ câu hỏi của bạn những gì bạn đang cố gắng làm hoặc hiểu về tập dữ liệu của bạn. Medians, percentiles vv là tuyệt vời cho các bản phân phối bình thường và họ loại bỏ các ngoại lệ cực kỳ với rất ít rắc rối. Bạn có chắc là bạn có phân phối bình thường không? Nghe có vẻ giống như bạn có thể có một phân phối mũ. Sẽ hữu ích hơn khi nói những gì bạn đang cố gắng hiểu về dữ liệu của bạn hơn là hỏi về các chức năng. –

Trả lời

0

Để tạo phân phối tuyến tính hơn, tôi đã thêm cột được tính vào bảng dữ liệu, HITS_SQRT HITS_SQRT AS (CONVERT([int],sqrt(HITS*4),(0))) PERSISTED.

Sử dụng cột này, bạn có thể tính số mục tiêu là "số lần truy cập trên phần trăm".

select @hitsPerGroup=SUM(HITS_SQRT)/(@numGroups -1)[email protected], @dataPoints=COUNT(*) FROM #Rank_Table 

Kịch bản sau đó tạo ra một bảng tạm thời với một ROW_NUMBER() sắp xếp theo số lượng truy cập và lặp các hàng theo thứ tự cập nhật phần trăm của nó từ 100 đến 1. giảm dần Tổng cộng chạy được giữ số lượng hit và khi số @hitsPerGroup được chuyển, phần trăm được hạ xuống từ 100 đến 99, 99 đến 98, v.v.

Sau đó bảng dữ liệu nguồn được cập nhật với phần trăm của nó. Có một chỉ mục của bảng làm việc tạm thời để tăng tốc độ cập nhật.

Toàn bộ tập lệnh bằng cách sử dụng #Rank_Table làm bảng dữ liệu nguồn.

--Create Test Data 
CREATE TABLE #Rank_Table(
id int identity(1,1) not null, 
hits bigint not null default 0, 
PERCENTILE smallint NULL, 
HITS_SQRT AS (CONVERT([int],sqrt(HITS*4),(0))) PERSISTED 
) 
--Slant the distribution of the data 
INSERT INTO #Rank_Table (hits) 
select CASE 
    when DATA > 9500 THEN DATA*30 
    WHEN data > 8000 THEN DATA*5 
    WHEN data < 7000 THEN DATA/3 +1 
    ELSE DATA 
END 
FROM 
(select top 10000 (ABS(CHECKSUM(NewId())) % 99 +1) * (ABS(CHECKSUM(NewId())) % 99 +1) DATA 
from master..spt_values t1 
    cross JOIN master..spt_values t2) exponential 

--Create temp work table and variables to calculate percentiles 
    Declare @hitsPerGroup as int 
    Declare @numGroups as int 
    Declare @dataPoints as int 
    set @numGroups=100 

    select @hitsPerGroup=SUM(HITS_SQRT)/(@numGroups -1)[email protected], @dataPoints=COUNT(*) FROM #Rank_Table 

    --show the number of hits that each group should have 
    select @hitsPerGroup HITS_PER_GROUP 

    --Use temp table for the calculation 
    CREATE TABLE #tbl (
     row int, 
     hits int, 
     ID bigint, 
     PERCENTILE smallint null 
    ) 
    --add index to row 
    CREATE CLUSTERED INDEX idxRow ON #tbl(row) 

    insert INTO #tbl 
    select ROW_NUMBER() over (ORDER BY HITS), hits_SQRT, ID, null from #Rank_Table 

    --Update each row with a running total. 
    --lower the percentile by one when we cross a threshold for the maximum number of hits per group (@hitsPerGroup) 
    DECLARE @row as int 
    DEClare @runningTotal as int 
    declare @percentile int 
    set @row = 0 
    set @runningTotal = 0 
    set @percentile = @numGroups 

    while @row <= @dataPoints 
    BEGIN 
     select @[email protected] + hits from #tbl where [email protected] 

     if @runningTotal >= @hitsPerGroup 
     BEGIN 

      update #tbl 
      set [email protected] 
      WHERE PERCENTILE is null and row <@row 

      set @percentile = @percentile - 1 

      set @runningTotal = 0 
     END 

     --change rows 
     set @row = @row + 1 
    END 

    --get remaining 
    update #tbl 
    set [email protected] 
    WHERE PERCENTILE is null 

    --update source data 
    UPDATE m SET PERCENTILE = t.PERCENTILE 
    FROM #tbl t 
    inner join #Rank_Table m on t.ID=m.ID 


--Show the results 
    SELECT PERCENTILE, COUNT(id) NUMBER_RECORDS, SUM(HITS) HITS_IN_PERCENTILE 
    FROM #Rank_Table 
    GROUP BY PERCENTILE 
    ORDER BY PERCENTILE 

--cleanup 
    DROP TABLE #Rank_Table 
    DROP TABLE #tbl 

Hiệu suất không xuất sắc nhưng đạt được mục tiêu phân phối trượt mượt mà.

Các vấn đề liên quan