【问题标题】:SQL Server NTILE - Same value in different quartileSQL Server NTILE - 不同四分位数中的相同值
【发布时间】:2012-03-09 01:06:46
【问题描述】:

我有一个场景,我使用下面的 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

结果表如下图所示,A、B、C、D 4个类组之间有(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

【问题讨论】:

  • 这就是NTILE 的工作方式,如果你所有的基地都有相同的TotalStd 并且想要做四分位数会发生什么?无论如何它都会将值分成4个不同的组

标签: sql-server sql-server-2008


【解决方案1】:

您需要使用 rank 函数重新创建 Ntile 函数。 rank 函数为具有相同值的行提供相同的排名。该值稍后会“跳”到下一个等级,就像您使用 row_number 一样。 我们可以使用这种行为来模仿 Ntile 函数,强制它为具有相同值的行赋予相同的 Ntile 值。但是 - 这将导致 Ntile 分区的大小不同。 请参阅下面的示例,了解使用 4 个 bin 的新 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

【讨论】:

  • 你能解释一下它是如何工作的吗? partition by (select 1) 应该做什么?
  • “over partition by”子句对“partition by”列中的每个值运行“count(1)”聚合。假设您有一个包含 3 个字段的聚合销售表 - store、weekday、sold_amount。您想知道每个工作日每家商店的销售额份额。您可以使用此查询:“选择商店,工作日,sold_amount / sum(sold_amount) over (partition by weekday)”。如果您没有工作日,要运行 over 分区,您需要指定一个分区列。既然没有,你就用这个技巧:“select store, sold_amount / sum(sold_amount) over (partition by (select 1))
  • 除法两边是否必须使用相同的partition by?我的意思是:1+(rank() over (partition by store, weekday order by sales)-1) * 4 / count(1) over (partition by store, weekday)
  • 如果你想在这些组中运行 Ntile - 那么是的。
  • 我认为您的解决方案不能保证相同的销售额不会跳入两个不同的板块。比如说,我有一半的销售案例具有相同的价值,而其他销售案例则不同。所以我预计上半年的销售案例会跳到第一块。事实并非如此。或者我做错了什么。
【解决方案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 组与其旁边的组共享一个值!但是它还应该做什么呢?

【讨论】:

  • 我认为这里的问题是解决关系的次要变量是任意的。
【解决方案3】:

试试这个:

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

【讨论】:

    【解决方案4】:

    这里有一个 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
    

    【讨论】:

      猜你喜欢
      • 2022-06-14
      • 2021-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多