如何计算四分位数分组?

时间:2018-09-28 18:14:22

标签: sql sql-server sql-server-2008 quartile

假设我有一张桌子

VAL     PERSON
  1          1
  2          1
  3          1
  4          1
  2          2
  4          2
  6          2
  3          3
  6          3
  9          3
  12         3
  15         3

我想计算每个人的四分位数。

我知道我可以很容易地为一个人计算出这些值:

SELECT 
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 1;

将为我带来预期的结果:

VAL    QUARTILE
1      1
2      2
3      3
4      4

问题是,我想为每个人做到这一点。我知道像这样的事情会做的:

SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 1
UNION
SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 2
UNION
SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 3
UNION
SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 4

但是如果桌上有个新人怎么办?然后,我不得不更改SQL代码。有什么建议吗?

2 个答案:

答案 0 :(得分:5)

为什么不尝试使用分区依据。

SELECT 
  PERSON,
  VAL,
  NTILE(4) OVER(PARTITION BY PERSON ORDER BY VAL) AS QUARTILE;
FROM TABLE 

问候

答案 1 :(得分:-1)

ntile()处理关系不佳。您可以通过示例轻松看到这一点:

select v.x, ntile(2) over (order by x) as tile
from (values (1), (1), (1), (1)) v(x);

返回:

x tile
1   1
1   1
1   2
1   2

相同的值。不同的瓷砖。如果您要跟踪值所在的图块,则情况会变得更糟。即使同一行数据不变,不同的行在同一查询的不同运行上也可能具有不同的图块。

通常,即使瓦片的大小不同,您也希望具有相同值的行具有相同的四分位数。因此,我建议使用rank()进行显式计算:

select t.*,
       ((seqnum - 1) * 4 / cnt) + 1 as quartile
from (select t.*,
             rank() over (partition by person order by val) as seqnum,
             count(*) over (partition by person) as cnt
      from t
     ) t;

如果您实际上希望值在图块之间分割,请使用row_number()而不是rank()