拿到一个需求,让统计一下目前系统活跃用户各年龄段的人数。由于生产数据库是在内网,所以我提前用MySQL的语法写了测试语句。在MySQL上面测试结果完全正常,但是到了Oracle上面运行的时候报了各种错。最终的结果如下:
SELECT AGE_PROPORTION AS "年龄段", TO_NUMBER(COUNT(*)) AS "人数" FROM(
SELECT CASE
WHEN AGE>0 AND AGE<=10 THEN '0-10岁'
WHEN AGE>10 AND AGE<=20 THEN '10-20岁'
WHEN AGE>20 AND AGE<=30 THEN '20-30岁'
WHEN AGE>30 AND AGE<=40 THEN '30-40岁'
WHEN AGE>40 AND AGE<=50 THEN '40-50岁'
WHEN AGE>50 AND AGE<=60 THEN '50-60岁'
ELSE '60岁以上'
END AS AGE_PROPORTION,T.AGE FROM(
SELECT NUM.AGE AS AGE FROM (
SELECT I.IDNUMBER, (TO_CHAR(SYSDATE, 'yyyy') - SUBSTR(REGEXP_SUBSTR(I.IDNUMBER,'[0-9]+'), 7, 4)) AS AGE FROM (
SELECT U.IDNUMBER AS IDNUMBER FROM (
SELECT R.IDNUMBER,R.CREATE_TIME,ROW_NUMBER() OVER(PARTITION BY R.IDNUMBER ORDER BY R.CREATE_TIME DESC) RN FROM (
SELECT * FROM BARCODE_PAYMENT_RESULTS WHERE CREATE_TIME > '2020/9/1' AND LENGTH(IDNUMBER) = 18
) R
) U
WHERE U.RN = 1
) I
) NUM
) T
)A GROUP BY AGE_PROPORTION
我把SQL语句格式按照层级展开了方便阅读,首先从最里面开始看,因为需要取的是活跃用户,我对活跃的定义是交易记录表里面2020年9月1号以后产生交易的用户。因为历史遗留问题导致最初的用户没有登记身份证号码或者格式不正确,所以要先取身份证号码长度为18位的用户。接着取出这些用户的身份证号码和交易时间,并将结果按照创建时间倒序排列。因为一个用户可以有多条交易记录只取最新的一条来作为时间的判断依据,再然后将取出的身份证号码从第7位开始往后截取4位数字(这里遇到第一个坑)作为用户出生年份,然后用当前年份减去用户对应的出生年份计算用户目前的年龄,再通过指定条件判断归类用户所属年龄段的区间。最后按照年龄段进行分组,统计每个年龄段的具体人数就可以了。
排坑指南:
第一次运行SQL语句,系统提示说视图不存在
,找了半天最终发现在第一行给字段起别名的时候用的是汉字外面包裹的是单引号,然后改成了双引号……
后面再运行SQL语句,系统又提示ORA-01722: 无效数字
,但是我从第二行开始运行的时候又能完美显示。本来以为是最外面一层数字转换异常,在对应的字段外面加了TO_NUMBER()函数,依旧没能解决。最后百度了半小时,终于在一个2017年的提问下面找到了相同的情况,猛然惊醒,可能还是身份证号码的原因。可是我不是做了位数判断吗?嗯,因为身份证号码包含X。X是字母,这个字符的确不能转换为数字,所以用正则截取了一下身份证号码中的数字……
只能说Oracle在某些地方坑的一批,不仅YEAR()函数用不了,在对大小写和符号方面的限制也贼高。还是自己太菜了,真的是给跪了,一次难忘的回忆。