SQL优化之使用数学的方式优化SQL编码01

前言

笔者最近工作大多写sql为主。所以在此记录一下工作中遇到的问题和解决方案。

今天先写上一篇,之后也会将这类似的方法总结下来,发表在这里。

在特定场景下如何利用数学优化SQL编码

直接拿最近的一个需求举例,楼主习惯先写自己原先的笨方法,也是大多数人一下子就能想到的方法,然后在去写改进的方法。

好了,说下具体需求:

轻度汇总后的数据涉及到了标签打分。根据某列(数值型)的范围进行划分并打上相应的标签,并用该标签与标签评分表关联拿到评分。

看完需求后,简单啊,开搞。

为了方便,以下数据都用一个标签来讲。

  • 标签评分表一个标签对应了多个等级,(label, grade)唯一确定一条数据。如下:

    注意在我们的场景中当汇总表的total_countw 为0时,score分值也为0,并没有包含在前20%的区间中

label(标签)grade(等级)score(分值)desc( (左闭右开)
L1A1000前20%
L1B80020%—40%
L1C60040%—60%
L1D40060%—80%
L1E200后20%
L1F0值为0
  • 轻度汇总表table_agg:
idlabel(标签)total_count(该标签出现的次数)
001L1390
002L1700
003L1675
004L15065
  1. 首先需要将数据根据百分比将数据的等级标识出来
SELECT  id
       ,label
       ,total_count 
       ,rk
       ,cnt
       ,rk / cnt as percentage 
       ,CASE WHEN total_count = 0 THEN 'F' 
             WHEN rk / cnt > 0 AND rk / cnt < 0.2 THEN 'A' 
             WHEN rk / cnt >= 0.2 AND rk / cnt < 0.4 THEN 'B' 
             WHEN rk / cnt >= 0.4 AND rk / cnt < 0.6 THEN 'C' 
             WHEN rk / cnt >= 0.6 AND rk / cnt < 0.8 THEN 'D' 
             WHEN rk / cnt >= 0.8 THEN 'E' 
        END AS grade
FROM 
(
    SELECT  id
           ,label
           ,total_count
           ,row_number() OVER (ORDER BY total_count DESC) AS rk 
           ,COUNT(*) OVER ()                              AS cnt
    FROM table_agg 
) t;

结果如下:

idlabeltotal_countrkcnt_c5grade
004L15065180.125A
007L14078280.25B
002L1700380.375B
001L1390480.5C
008L1335580.625D
006L1201680.75D
005L199780.875E
003L10881F

然后根据(label, grade) 去关联标签评分表即可拿到对应的分值。

BUT

如果在0到1区县化分不只为5个区间呢?有时候为了让评分更加细粒度,我们可能会在0到1之前划分为100,500,甚至1000个区间!!

如果按照上面写case when的方式。。。emmm也挺好,一天啥也不用干了,泡杯茶CV大法。

但是这么做写出来的SQL别人看了会骂娘,而且也不保证区间的开闭都正确。

最重要的一点那就是:如果我的区间动态调整了,也就是说我的标签评分表更新了怎么办?人工维护吗???

非也!!下面介绍一种从数学上的方法,让你的代码由几十行,几百行都变成短短的一行。

改进

首先,在标签评分表中,如果我们的区间分为了n个,n > 50 …甚至1000,用大写字母ABC来表示已经很难了。

所以我们换种思路,将grade使用自然升序数字:1,2,3 … n 来表示,即:

label(标签)grade(等级)score(分值)desc( (左闭右开)
L111000前20%
L1280020%—40%
L1360040%—60%
L1440060%—80%
L15200后20%
L160值为0

然后重点来了,先把改完的sql列出来:

SELECT  id
       ,label
       ,total_count 
       ,rk
       ,cnt
       ,rk / cnt as percentage 
       ,CASE WHEN total_count = 0 THEN '0' ELSE ceil((rk / cnt) * 5) END AS grade
FROM 
(
    SELECT  id
           ,label
           ,total_count
           ,row_number() OVER (ORDER BY total_count DESC) AS rk 
           ,COUNT(*) OVER ()                              AS cnt
    FROM table_agg 
) t;

得到的结果为:

idlabeltotal_countrkcnt_c5grade
004L15065180.1251
007L14078280.252
002L1700380.3752
001L1390480.53
008L1335580.6254
006L1201680.754
005L199780.8755
003L108816

然后根据(label, grade) 去关联标签评分表即可拿到对应的分值。

大家看到了,没错!重点那就是

CASE WHEN total_count = 0 THEN '0' ELSE ceil((rk / cnt) * 5) END AS grade

这里使用了ceil这个向上取整的函数,而5则是代表了划分的区间。

至此,用数学方法解决了此类场景下根据百分数多个特定区间划分的问题。

总结

真是妙蛙种子吃着妙脆角妙进了米奇妙妙屋,妙到家了

by 俩只猴

2021.01.04

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值