SQL Server NTILE - 不同四分位数中的值相同

时间:2012-02-17 16:03:37

标签: sql-server sql-server-2008

我有一个场景,我使用下面的SQL Server NTILE函数将大量结果拆分为四分之一。目标是在每个类中具有相同数量的行

case NTILE(4) over (order by t2.TotalStd) 
   when 1 then 'A' when 2 then 'B' when 3 then 'C' else 'D' end as Class

结果表如下所示,4类A,B,C和D组之间存在(9,9,8,8)分割。

有两个结果导致我出现问题,两个行的总标准值都是30,但被分配到不同的四分位数。

8   30  A
2   30  B

我想知道有没有办法确保将具有相同值的行分配给相同的四分位数?我可以通过其他列进行分组或分区以获得此行为吗?

Pos TotalStd    class
1   16  A
2   23  A
3   21  A
4   29  A
5   25  A
6   26  A
7   28  A
8   30  A
9   29  A
1   31  B
2   30  B
3   32  B
4   32  B
5   34  B
6   32  B
7   34  B
8   32  B
9   33  B
1   36  C
2   35  C
3   35  C
4   35  C
5   40  C
6   38  C
7   41  C
8   43  C
1   43  D
2   48  D
3   45  D
4   47  D
5   44  D
6   48  D
7   46  D
8   57  D

4 个答案:

答案 0 :(得分:2)

不确定你期待什么在这里发生,真的。正如您所问,SQL Server已将数据划分为4个尽可能大小的组。您想要发生什么?看看这个例子:

declare @data table ( x int )

insert @data values 
(1),(2),
(2),(3),
(3),(4),
(4),(5)

select  
    x,
    NTILE(4) over (order by x) as ntile
from @data

结果:

x           ntile
----------- ----------
1           1
2           1
2           2
3           2
3           3
4           3
4           4
5           4

现在每个 ntile组与它旁边的那个共享一个值!但它还应该做些什么呢?

答案 1 :(得分:2)

您需要使用rank函数重新创建Ntile函数。 rank函数为具有相同值的行提供相同的排名。该值后来“跳转”到下一个排名,就好像您使用了row_number一样。 我们可以使用此行为来模仿Ntile函数,强制它为具有相同值的行提供相同的Ntile值。但是 - 这将导致Ntile分区具有不同的大小。 请参阅下面的示例,了解使用4个箱柜的新Ntile:

declare @data table ( x int )

insert @data values 
(1),(2),
(2),(3),
(3),(4),
(4),(5)

select  
    x,
    1+(rank() over (order by x)-1) * 4 / count(1) over (partition by (select 1)) as new_ntile
from @data

结果:

x   new_ntile
---------------
1   1
2   1
2   1
3   2
3   2
4   3
4   3
5   4

答案 2 :(得分:1)

试试这个:

; with a as (
                select TotalStd,Class=case ntile(4)over( order by TotalStd )
                                when 1 then 'A'
                                when 2 then 'B'
                                when 3 then 'C'
                                when 4 then 'D'
                                end
                from t2
                group by TotalStd
)
select d.*, a.Class from t2 d
inner join a on a.TotalStd=d.TotalStd
order by Class,Pos;

答案 3 :(得分:0)

这里我们有一个34行的表。

DECLARE @x TABLE (TotalStd INT) 
INSERT @x (TotalStd) VALUES (16), (21), (23), (25), (26), (28), (29), (29), (30), (30), (31), (32), (32), (32), (32), (33), (34), 
    (34), (35), (35), (35), (36), (38), (40), (41), (43), (43), (44), (45), (46), (47), (48), (48), (57)
SELECT '@x', TotalStd FROM @x ORDER BY TotalStd

我们想分成四分位数。如果我们使用NTILE,则桶大小将大致相同(每个8到9行),但是关系可以任意划分:

SELECT '@x with NTILE', TotalStd, NTILE(4) OVER (ORDER BY TotalStd) quantile FROM @x

了解 30 如何出现两次:一次在分位数1中,一次在分位数2中。同样, 43 同时出现在分位数3和4中。

我应该找到的是分位数1中的10个项目,分位数2中的8个,分位数3中的7个和分位数4中的9个(即不是完美的9-8-9-8分割,但如果不是这样的分割,则不可能我们不允许任意打破关系)。我可以使用NTILE来确定临时表中的截止点:

DECLARE @cutoffs TABLE (quantile INT, min_value INT, max_value INT)

INSERT @cutoffs (quantile, min_value)
SELECT y.quantile, MIN(y.TotalStd)
FROM (SELECT TotalStd, NTILE(4) OVER (ORDER BY TotalStd) AS quantile FROM @x) y
GROUP BY y.quantile

-- The max values are the minimum values of the next quintiles
UPDATE c1 SET c1.max_value = ISNULL(C2.min_value, (SELECT MAX(TotalStd) + 1 FROM @x))
FROM @cutoffs c1 LEFT OUTER JOIN @cutoffs c2 ON c2.quantile - 1 = c1.quantile

SELECT '@cutoffs', * FROM @cutoffs

我们将使用@cutoffs表中的边界值来创建最终表:

SELECT x.TotalStd, c.quantile FROM @x x 
    INNER JOIN @cutoffs c ON x.TotalStd >= c.min_value AND x.TotalStd < c.max_value