首页 > 数据库 >【PostageSQL】SQL每行后边拼接总数以及利用最大余额法计算占比

【PostageSQL】SQL每行后边拼接总数以及利用最大余额法计算占比

时间:2023-06-01 15:33:58浏览次数:36  
标签:分数 每行 num -- 整数 PostageSQL score SQL 小数

1  前言

最近碰上个需求,因为那个报表工具只能写SQL,所以需要用SQL计算每条数据占总和的百分比,比如下边这样,要对每个人的分数计算占总和的百分比。

2  问题思路

我们首先要有个思路:

  • 每行后边要先统计一个总数(用以求出占比)
  • 占比存在取舍,会导致总的占比和大于或者小于100%,所以这里我们需要用最大余额法来进行调整

3  最大余额法

有了思路,我们先看下最大余额法是个什么计算过程哈,比如Echarts里的饼状图的占比,我们举个例子你应该就明白了:

假设有数组 array = [7, 18, 23, 17],要求计算每个数字占总和的百分比。

普通计算方式:

(1)计算总和 7 + 18 + 23 + 17 = 65

(2)每个数依次除以总和并乘以100
  7 / 65 * 100 = 10.76923076923077
  18 / 65 * 100 = 27.692307692307693
  23 / 65 * 100 = 35.38461538461539
  17 / 65 * 100 = 26.153846153846157

(3)四舍五入保留两位小数

  10.76923076923077 => 10.77
  27.692307692307693 => 27.69
  35.38461538461539 => 35.38
  26.153846153846157 => 26.15

最终结果 10.77 + 27.69 + 35.38 + 26.15 = 99.99 < 100

最大余额法:

(1)计算总和 7 + 18 + 23 + 17 = 65

(2)计算总份额

  由于计算的是百分比,所以需要扩大100倍
  百分比保留两位小数,所以再扩大100倍(三位小数就是1000倍)
  所以总份额为 100 * 100 = 10000

(3)按比例分配份额

  7 / 65 * 10000 = 1076.923076923077
  18 / 65 * 10000 = 2769.2307692307693
  23 / 65 * 10000 = 3538.461538461539
  17 / 65 * 10000 = 2615.3846153846157

(4)取分配份额的整数部分 [1076, 2769, 3538, 2615]

  1076 + 2769 + 3538 + 2615 = 9998 < 10000
  10000 - 9998 = 2 因此还剩两份需要分配

(5)取分配份额的小数部分 [0.923076923077, 0.2307692307693, 0.461538461539, 0.3846153846157]

  找出小数部分最大的两个数(剩几份找几个),下标分别为0 和 2

(6)为整数部分下标为0 和 2的数各加上1

  [1076 + 1, 2769, 3538 + 1, 2615] => [1077, 2769, 3539, 2615]

(7)除以100得到最终的百分比

  因为保留2位小数,之前乘以了100,所以最后要除以100
  [1077, 2769, 3539, 2615] / 100 => [10.77, 27.69, 35.39, 26.15]

最终结果 10.77 + 27.69 + 35.39 + 26.15 = 100

4  问题拆解

那我们回到问题,我们继续看下怎么求出占比:

(1)第一步我们首先要在每行后边拼接一个总分数和,这里我们可以用到窗口函数,我们来看:

SELECT 
    NAME,  -- 名字
    score, -- 分数
    CAST ( SUM ( score ) OVER ( PARTITION BY 1 ) AS NUMERIC ( 16, 0 ) ) total --分数和 这里为什么要转为小数呢 是因为等下除法都是整数的话也会是整数
FROM
    cc_score

(2)有了分数总和以后我们就可以按照最大余额法得出分配份额:

SELECT 
    NAME,
    score,
    total, -- 分数和
    score / t1.total * 10000 assign_percent -- 分配份额
FROM
    (
    SELECT 
        NAME,  -- 名字
        score, -- 分数
        CAST ( SUM ( score ) OVER ( PARTITION BY 1 ) AS NUMERIC ( 16, 0 ) ) total --分数和 这里为什么要转为小数呢 是因为等下除法都是整数的话也会是整数
    FROM
    cc_score 
    ) t1

(3)有了分配份额我们就可以得到整数部分和小数部分:

SELECT 
    NAME,
    score,
    total, -- 分数和
    assign_percent, -- 分配份额
    FLOOR ( assign_percent ) down_num, -- 取整数部分
    assign_percent - FLOOR ( assign_percent ) small_num -- 取小数部分
FROM (
    SELECT 
        NAME,
        score,
        total, -- 分数和
        score / t1.total * 10000 assign_percent -- 分配份额
    FROM
        (
        SELECT 
            NAME,  -- 名字
            score, -- 分数
            CAST ( SUM ( score ) OVER ( PARTITION BY 1 ) AS NUMERIC ( 16, 0 ) ) total --分数和 这里为什么要转为小数呢是因为等下除法都是整数的话也会是整数
        FROM
        cc_score 
        ) t1
) t1

(4)整数部分和小数部分有了我们就可以计算整数部分的和以及小数部分进行倒序标号:

SELECT 
    NAME,
    score,
    total, -- 分数和
    assign_percent, -- 分配份额
    down_num, -- 取整数部分
    small_num,  -- 取小数部分
    SUM ( down_num ) OVER ( PARTITION BY 1 ) down_num_sum, -- 整数部分求和
    ROW_NUMBER () OVER ( PARTITION BY 1 ORDER BY small_num DESC ) row_num --小数部分倒序标注行号
FROM (
    SELECT 
        NAME,
        score,
        total, -- 分数和
        assign_percent, -- 分配份额
        FLOOR ( assign_percent ) down_num, -- 取整数部分
        assign_percent - FLOOR ( assign_percent ) small_num -- 取小数部分
    FROM (
        SELECT 
            NAME,
            score,
            total, -- 分数和
            score / t1.total * 10000 assign_percent -- 分配份额
        FROM
            (
            SELECT 
                NAME,  -- 名字
                score, -- 分数
                CAST ( SUM ( score ) OVER ( PARTITION BY 1 ) AS NUMERIC ( 16, 0 ) ) total --分数和 这里为什么要转为小数呢是因为等下除法都是整数的话也会是整数
            FROM
            cc_score 
            ) t1
    ) t1
) t1

(5)接下来我们就可以判断要对那几个整数部分进行+1,并除以100得到所占的百分比啦:

SELECT 
    NAME 名字,
    score 分数,
    total 分数总和, -- 分数和
    assign_percent 分配份额, -- 分配份额
    down_num 份额整数部分, -- 取整数部分
    small_num 份额小数部分,  -- 取小数部分
    down_num_sum 份额整数部分之和, -- 整数部分求和
    row_num 小数部分倒序号, -- 小数部分倒序标注行号
    num 剩多少需要分配, -- 需要进行分配的个数
    (
        CASE WHEN row_num <= num THEN round( ( down_num + 1 ) / 100, 2 ) 
        ELSE round( down_num / 100, 2 ) 
        END  
  ) 占比
FROM (
    SELECT 
        NAME,
        score,
        total, -- 分数和
        assign_percent, -- 分配份额
        down_num, -- 取整数部分
        small_num,  -- 取小数部分
        down_num_sum, -- 整数部分求和
        row_num, -- 小数部分倒序标注行号
        10000 - down_num_sum num -- 需要进行分配的个数
    FROM (
        SELECT 
            NAME,
            score,
            total, -- 分数和
            assign_percent, -- 分配份额
            down_num, -- 取整数部分
            small_num,  -- 取小数部分
            SUM ( down_num ) OVER ( PARTITION BY 1 ) down_num_sum, -- 整数部分求和
            ROW_NUMBER () OVER ( PARTITION BY 1 ORDER BY small_num DESC ) row_num --小数部分倒序标注行号
        FROM (
            SELECT 
                NAME,
                score,
                total, -- 分数和
                assign_percent, -- 分配份额
                FLOOR ( assign_percent ) down_num, -- 取整数部分
                assign_percent - FLOOR ( assign_percent ) small_num -- 取小数部分
            FROM (
                SELECT 
                    NAME,
                    score,
                    total, -- 分数和
                    score / t1.total * 10000 assign_percent -- 分配份额
                FROM
                    (
                    SELECT 
                        NAME,  -- 名字
                        score, -- 分数
                        CAST ( SUM ( score ) OVER ( PARTITION BY 1 ) AS NUMERIC ( 16, 0 ) ) total --分数和 这里为什么要转为小数呢是因为等下除法都是整数的话也会是整数
                    FROM
                    cc_score 
                    ) t1
            ) t1
        ) t1
    ) t1
) t1

最后归纳一下SQL:

SELECT 
    NAME 名字,
    score 分数,
    total 分数总和, -- 分数和
    assign_percent 分配份额, -- 分配份额
    down_num 份额整数部分, -- 取整数部分
    small_num 份额小数部分,  -- 取小数部分
    down_num_sum 份额整数部分之和, -- 整数部分求和
    row_num 小数部分倒序号, -- 小数部分倒序标注行号
    num 剩多少需要分配, -- 需要进行分配的个数
    (
        CASE WHEN row_num <= num THEN round( ( down_num + 1 ) / 100, 2 ) 
        ELSE round( down_num / 100, 2 ) 
        END  
  ) 占比
FROM (
    SELECT
        name,
        score,
        total, 
        score / t1.total * 10000 assign_percent,
        FLOOR ( score / t1.total * 10000 ) down_num,
        score / t1.total * 10000 - FLOOR ( score / t1.total * 10000 ) small_num,
        SUM ( FLOOR ( score / t1.total * 10000 ) ) OVER ( PARTITION BY 1 ) down_num_sum,
        10000 - SUM ( FLOOR ( score / t1.total * 10000 ) ) OVER ( PARTITION BY 1 ) num, 
        ROW_NUMBER ( ) OVER ( PARTITION BY 1 ORDER BY (score / t1.total * 10000 - FLOOR ( score / t1.total * 10000 )) DESC ) row_num 
    FROM
        ( SELECT NAME , score , CAST(SUM ( score ) OVER ( PARTITION BY 1 ) AS NUMERIC(16, 0)) total FROM cc_score ) t1
) t1

5  小结

好啦,占比我们就计算完了哈,这种只适用于我们统计哈,可以看到很多的函数很不友好,所以通常用于我们自己统计哦,有理解不对的地方欢迎指正哈。

 

标签:分数,每行,num,--,整数,PostageSQL,score,SQL,小数
From: https://www.cnblogs.com/kukuxjx/p/17448830.html

相关文章

  • MySQL读写分离
    云数据库RDS版>用户指南>读写分离>验证读写分离效果>通过内部SQL命令验证https://help.aliyun.com/document_detail/54526.html?spm=5176.doc54538.6.698.kpQGbW ......
  • MYSQL知识点汇聚
    MySQL社区版下载地址:http://dev.mysql.com/downloads/第二版MYSQL视频教程:http://php.itcast.cn/news/20130617/17423736508.shtmlMYSQL优化视频教程:http://php.itcast.cn/news/61ee8515/a34e/477d/9d5d/662dbff5e161.shtml 1、MYSQL如何设置大小写敏感写道1、linu......
  • sql之left join、right join、inner join的区别
    sql之leftjoin、rightjoin、innerjoin的区别 sql之leftjoin、rightjoin、innerjoin的区别leftjoin(左联接)返回包括左表中的所有记录和右表中联结字段相等的记录 rightjoin(右联接)返回包括右表中的所有记录和左表中联结字段相等的记录innerjoin(等值连接)只返回......
  • mysql innodb_lock
    SHOWVARIABLESLIKE'%innodb_lock%'    ......
  • Mysql json数组解析方法
    一、背景在表job_position需要对json数组进行解析,查找json数组对象中的数据 首先想到查找mysql的关于json的函数,这边做个记录。二、解决方案2.1 JSON_EXTRACT(json字段,'$.属性名称')需要了解函数 JSON_EXTRACT(json_doc, path[, path]...)  从json中提取数据,JSON_E......
  • mysql主从复制
    mysql主从复制mysql主从复制的分类全同步复制在全同步复制中,主库(master)写数据到二进制日志(binlog)且同步(sync),所有从库请求二进制日志(slaverequestbinlog)后写入中断日志(relay-log)并flushdisk,并且应用完日志且commit。理解︰当主提交一个事务,在主库向前端业......
  • 如何使用PL/SQL Developer 连接remote 数据库
    https://www.allroundautomations.com/plsqldev.html 1.下载并安装OracleInstantClient Free,light-weightandeasilyinstalledOracleDatabaselibrariesandSDKsforbuildingandconnectingclientapplicationstolocalorremoteOracleDatabases.可以通过......
  • MySQL视图
    一:视图概述1:为什么使用视图视图可以帮我们使用表的一部分而不是所有的表,另一方面也可以针对不同的用户制定不同的查询视图。比如,针对一个公司的销售人员,我们只想给他看部分数据,而某些特殊的数据,比如采购的价格,则不会提供给他。再比如,人员薪酬是个敏感的字段,那么只给某个级别......
  • 10条sql语句掌握
    10个必须掌握的SQL常用语句原创 RankFan 经济知识综合 2022-12-2411:08 发表于福建收录于合集#sql1个##Medium翻译17个世界上70%的相关性数据可以被储存为表格数据,即类似Excel,Csv类型的数据,如何去查询和分析相关性的数据?对于少量数据,我们可以使用Excel,更多一......
  • Navicat远程链接mysql数据库问题
    问题描述:docker安装mysql数据后使用Navicat链接mysql后,创建数据库报“1044-Accessdenidforuser'root'...”解决方案:1.登录远程服务器,进入mysql容器dockerexec-itmysql/bin/bash2.mysql-uroot-p->grantallprivilegeson*.*to'root'@'%';->flush......