【问题标题】:Analyse and form quantiles in SQL and count the percentage of values that fall in respective quantiles在 SQL 中分析并形成分位数并计算落在各个分位数中的值的百分比
【发布时间】:2021-01-27 15:07:26
【问题描述】:

您好,我在 MySQL 数据库中有以下数据

txnid amount type
    1    500 A
    2    600 B
    3    700 A
    4    800 C
    5    900 D
    6   1000 A
    7   1100 B
    8   1200 B

我想在第一个 bin 中构建四个包含 0-25 分位数的 bin,在第二个 bin 中构建 25-50 个分位数,依此类推。接下来我要计算落在各个 bin 中的值的百分比。

【问题讨论】:

标签: mysql sql count pivot aggregation


【解决方案1】:

如果我正确地遵循了这一点,您可以使用ntile() 和算术来构建存储桶,然后使用条件聚合来透视:

select min_amount, max_amount, 
    sum(type = 'A') / total_cnt a,
    sum(type = 'B') / total_cnt b,
    sum(type = 'C') / total_cnt c,
    sum(type = 'D') / total_cnt d,
    count(*) / total_cnt all_types
from (
    select t.*, 
        min(amount) over() + (ntile(4) over(order by amount) - 1) / 4 * (max(amount) over() - min(amount) over()) min_amount,
        min(amount) over() +  ntile(4) over(order by amount)      / 4 * (max(amount) over() - min(amount) over()) max_amount,
        count(*) over() total_cnt
    from mytable t
) t
group by min_amount, max_amount

我将存储桶边界放在两个不同的列中,因为这对我来说更有意义 - 如果您愿意,可以将它们连接起来。比率表示为01 之间的值,如果需要百分比,可以乘以100

Demo on DB Fiddle

最小金额 |最大金额 |一个 |乙 | c | d |所有类型 ---------: | ---------: | -----: | -----: | -----: | -----: | --------: 500.0000 | 675.0000 | 0.1250 | 0.1250 | 0.0000 | 0.0000 | 0.2500 675.0000 | 850.0000 | 0.1250 | 0.0000 | 0.1250 | 0.0000 | 0.2500 850.0000 | 1025.0000 | 0.1250 | 0.0000 | 0.0000 | 0.1250 | 0.2500 1025.0000 | 1200.0000 | 0.0000 | 0.2500 | 0.0000 | 0.0000 | 0.2500

【讨论】:

  • 嗨,我不能在查询中使用 over()。我正在使用一个名为元数据库的可视化应用程序,并且在使用 over() 时它给出了 SQL 语法错误
【解决方案2】:

需要首先确定结果中每一行的间隔。由于表中没有这样的值。可能会形成一个包含UNION ALL 运算符的子查询。然后应该构造一个动态查询,考虑到type 列的不同值可能会更改。因此,您可以使用以下语句:

SET SESSION group_concat_max_len = 4096;
SET @sql = NULL;

SELECT GROUP_CONCAT(
       CONCAT('CONCAT(ROUND( 100 * SUM( CASE WHEN amount BETWEEN THEN type = ''',type,''' END)/COUNT(*), 2),"%") AS ',type )
       )
  INTO @sql
  FROM ( SELECT DISTINCT type FROM t) tt;

SELECT GROUP_CONCAT(
       CONCAT(
               'SELECT "',REPLACE(intrval,' AND ','-'),'" AS "Amount Bins", ',
                          REPLACE( @sql, 'BETWEEN THEN', 
                                  CONCAT('BETWEEN ',intrval,' THEN' )),
                      ', CONCAT(ROUND( 100 * SUM( CASE WHEN amount BETWEEN ',intrval,' THEN 1 END)/COUNT(*), 2),"%") AS "All Types"',         
               ' FROM t '                    
              )
       SEPARATOR ' UNION ALL '       
       )
  INTO @sql
  FROM (SELECT @i := @i + 1 AS i, 
               CONCAT(@i * ((max_amount - min_amount) / 4) + min_amount,' AND ',
                     (@i + 1) * ((max_amount - min_amount) / 4) + min_amount ) AS intrval
          FROM information_schema.tables i
          JOIN (SELECT @i := -1) AS ii
          JOIN (SELECT MIN(amount) AS min_amount, MAX(amount) AS max_amount
                  FROM t ) AS t_agg  
         WHERE @i < 3) i;

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

结果是

Amount Bins A       B       C       D       All Types
----------- ------  ------  ------  ------  ---------
500-675     12.50%  12.50%  0.00%   0.00%   25.00%
675-850     12.50%  0.00%   12.50%  0.00%   25.00%
850-1025    12.50%  0.00%   0.00%   12.50%  25.00%
1025-1200   0.00%   25.00%  0.00%   0.00%   25.00%

Demo

【讨论】:

  • 这行不通,因为我们需要动态确定间隔,而不是在查询中对其进行硬编码
  • 但在问题@NikhilKumarSingh 中没有区间值的来源(例如表格)或确定逻辑
  • 我们需要分配分位数,比如第一个范围是 0%-25% 分位数,第二个是 25%-50% 分位数,第三个是 50%-75% 分位数,最后一个将是 75%-100%(最大)分位数值。
  • 好吧,但是如何确定这些区间值 500-674、675-849 .. 等?我们如何知道这些价值观?它们是预定的吗?是否有规则让他们保持在每个分位数内? @NikhilKumarSingh
  • 这就是我们的问题所在。我们需要编写一个查询来确定这些值。如果您查看@GMB 的答案。他使用每个分位数的最小和最大数量找到分位数
猜你喜欢
  • 2012-10-28
  • 1970-01-01
  • 2013-05-23
  • 2020-10-07
  • 2017-12-03
  • 1970-01-01
  • 2017-10-12
  • 2017-10-19
  • 1970-01-01
相关资源
最近更新 更多