常用统计分析 SQL 在 AWK 中的实现
最近有需求需要本地处理一些临时的数据,用做统计分析。如果单纯的 MYSQL 也能实现, 不过一堆临时数据这样从 mysql 导来导去还是挺麻烦的,比较理想的选择是本机装个 cygwin 环境,然后可以用 awk 等 shell 工具做即时处理。
本文主要讲述如何在 awk 中实现 SQL 的常用操作,当做个简单的 awk 入门分享。 虽然文中部分 awk 会有其它更简洁高效的 shell 命令去完成,亦或是其它语言去完成, 但这都不在本文的讨论范畴。
注:本文所用到的两个测试文件 user、consumer,分别模拟两张 SQL 表:
user 表,字段: id name addr 1 zhangsan hubei 3 lisi tianjin 4 wangmazi guangzhou 2 wangwu beijing consumer 表,字段: id cost date 1 15 20121213 2 20 20121213 3 100 20121213 4 99 20121213 1 25 20121114 2 108 20121114 3 100 20121114 4 66 20121114 1 15 20121213 1 115 20121114 测试环境: OS 版本: uname -a CYGWIN_NT-6.1 june-PC 1.7.9(0.237/5/3) 2011-03-29 10:10 i686 Cygwin awk 版本: awk --version GNU Awk 3.1.8
1、查询整张表记录,where 条件过滤,关键词:where
select * from user;
awk 1 user;
select * from consumer where cost > 100;
awk '$2>100' consumer
2、对某个字段去重,或者按记录去重,关键词:distinct
select distinct(date) from consumer;
awk '!a[$3]++{print $3}' consumer
select distinct(*) from consumer;
awk '!a[$0]++' consumer
3、记录按序输出,关键词:order by
select id from user order by id;
awk '{a[$1]}END{asorti(a);for(i=1;i<=length(a);i++){print a[i]}}' user
4、取前多少条记录,关键词:limit
select * from consumer limit 2;
awk 'NR<=2' consumer
awk 'NR>2{exit}1' consumer # performance is better
5、分组求和统计,关键词:group by、having、sum、count
select id, count(1), sum(cost) from consumer group by id having count(1) > 2;
awk '{a[$1]=a[$1]==""?$2:a[$1]","$2}END{for(i in a){c=split(a[i],b,",");if(c>2){sum=0;for(j in b){sum+=b[j]};print i"t"c"t"sum}}}' consumer
6、模糊查询,关键词:like(like属于通配,也可正则 REGEXP)
select name from user where name like 'wang%';
awk '$2 ~/^wang/{print $2}' user
select addr from user where addr like '%bei';
awk '/.*bei$/{print $3}' user
select addr from user where addr like '%bei%';
awk '$3 ~/bei/{print $3}' user
7、多表 join 关联查询,关键词:join
select a.* , b.* from user a inner join consumer b on a.id = b.id and b.id = 2;
awk 'ARGIND==1{a[$1]=$0;next}{if(($1 in a)&&$1==2){print a[$1]"t"$2"t"$3}}' user consumer
8、多表水平联接,关键词:union all
select a.* from user a union all select b.* from user b;
awk 1 user user
select a.* from user a union select b.* from user b;
awk '!a[$0]++' user user
9、随机抽样统计,关键词:order by rand()
SELECT * FROM consumer ORDER BY RAND() LIMIT 2;
awk 'BEGIN{srand();while(i<2){k=int(rand()*10)+1;if(!(k in a)){a[k];i++}}}(NR in a)' consumer
10、行列转换,关键词:SUM(IF())、WITH ROLLUP
mysql 写法:http://my.oschina.net/leejun2005/blog/77796 awk 写法:http://hi.baidu.com/leejun_2005/item/2bac30c2b97e5e56ad00ef86
11、awk 小应用之 RTX 订餐统计:
1、功能: 统计 rtx 聊天记录中的订餐信息,包括且限于:菜名、人员姓名、人数 2、支持的功能: 订餐、取消、修改 3、格式: 订餐:“+1 空格 菜名”,如: “+1 鸡腿” // 不含双引号 取消:“-1” 即可, 如: “-1” // 不含双引号 修改:格式同订餐一样,会自动根据姓名覆盖 4、使用限制与注意事项: (1)必须严格遵守格式,否则会统计错误,例如:菜名和+1-1之间要空格分隔,且必须 -1+1 开头 (2)如果一人代订多人,需要复制格式,修改姓名,然后发布多条信息, 格式: //代订 // 这一行一定要带上,不能以 +-( 字符开头 (userName) //要以 ( 打头,如果你自己点多份,请在名字后面带上数字序号,如 userName1 +1 菜名 暂不支持直接 “+2 菜名” 这种形式,因为最后需要按姓名汇总 (3)此 awk 脚本需要在 4.0 版本以上运行,因为 4.0 以下的 HashMap 不支持中文 key。 测试用例: echo " user(统计测试) 18:30:52 对吧 user(统计测试) 18:30:55 下单了,嗯 user(张三) 18:31:11 +1 西瓜泡方便面 user(统计测试) 18:30:52 对吧 -1 测试 user(统计测试) 18:30:52 // 这是帮人代订的测试,这行一定要,随便写点啥都行 -------------------- 测试代订功能 (代订测试人) +1 豆腐脑-甜的 。。。。。。。。。 (代订测试人2) +1 豆腐脑-酸的 user(统计测试) 18:30:55 下单了,嗯 user(李四) 18:31:11 +1 大排 user(李四) 18:31:11 -1 user(统计测试) 18:30:52 对吧 user(统计测试) 18:30:55 下单了,嗯 user(张三) 18:31:11 +1 带鱼 user(王麻子) 18:31:11 +1 大蒜 user(统计测试) 18:30:55 下单了,嗯 user(测试程序) 18:31:11 +1 唐僧肉 user(测试程序1) 18:31:11 +1 带鱼 user(赵六) 18:31:11 +1 大蒜 "| awk '/(/{gsub(/.*(|).*/,"");name=$0;getline;if(!($0~/^(+|-)/))next;a[name]=$0}END{for(i in a){split(a[i],b," ");if(b[2]=="")continue;c[b[2]]=c[b[2]]==""?i:c[b[2]]","i};for(i in c){split(c[i],d,",");print i":t"c[i]"t"length(d)}}'|column -t 结果: 带鱼: 测试程序1,张三 2 唐僧肉: 测试程序 1 大蒜: 赵六,王麻子 2 豆腐脑-酸的: 代订测试人2 1 豆腐脑-甜的: 代订测试人 1
12、查找父ID
echo "1 0
11 1
111 11
1111 111"|awk '{a[$1]=$2;if($2==0){b[$1]=$12}}END{for(i in a){j=i;c=0;while(a[j]!=0){j=a[j];c++};print i"t"j"t"c}}'
结果:
id rootId level
1111 1 3
111 1 2
11 1 1
1 1 0
关于 id 间父子关系的建立与查找,还可以参考这个例子中的 python 写法:
python 数据结构转换,将线性元祖转换成字典树:
http://segmentfault.com/q/1010000000415526
t = (
(1, -1, 'python'),
(2, -1, 'ruby'),
(3, -1, 'php'),
(4, -1, 'lisp'),
(5, 1, 'flask'),
(6, 1, 'django'),
(7, 1, 'webpy'),
(8, 2, 'rails'),
(9, 3, 'zend'),
(10, 6, 'dblog')
)
# fid 无序版
from itertools import groupby
from operator import itemgetter as get
from pprint import pprint
# group by fid
tmp = dict([(k, list(rows)) for k, rows in groupby(sorted(t, key=get(1)), get(1))])
def map_fun(row):
item = dict(zip(('id', 'fid', 'title'), row))
if row[0] in tmp:
item['son'] = find_children(row[0])
return item;
def find_children(parent):
return map(map_fun, tmp[parent])
pprint(find_children(-1))
t = (
(1, -1, 'python'),
(2, -1, 'ruby'),
(3, -1, 'php'),
(4, -1, 'lisp'),
(5, 1, 'flask'),
(6, 1, 'django'),
(7, 1, 'webpy'),
(8, 2, 'rails'),
(9, 3, 'zend'),
(10, 6, 'dblog')
)
# fid 有序版
from pprint import pprint
l = []
entries = {}
for id, fid, title in t:
entries[id] = entry = {'id': id, 'fid': fid, 'title': title}
if fid == -1:
l.append(entry)
else:
parent = entries[fid]
parent.setdefault('son', []).append(entry)
pprint(l)
【updating】 本文将会不定期更新。。。
推荐阅读:
[1] 更快的IP库查找方法以及AWK中的二分查找
http://blogread.cn/it/article/6369?f=wb
[2] q - Text as Data
http://harelba.github.io/q/index.html
q is a command line tool that allows direct execution of SQL-like queries on CSVs/TSVs (and any other tabular text files). q treats ordinary files as database tables, and supports all SQL constructs, such as WHERE, GROUP BY, JOINs etc. It supports automatic column name and column type detection, and provides full support for multiple encodings.
q "SELECT COUNT(*) FROM ./clicks_file.csv WHERE c3 > 32.3"
ps -ef | q -H "SELECT UID,COUNT(*) cnt FROM - GROUP BY UID ORDER BY cnt DESC LIMIT 3"
[3] 数据工程师常用的 Shell 命令
http://blog.jobbole.com/99034/
[4] awk入门.md
[5] AWK程序设计语言
http://awk.readthedocs.io/en/latest/chapter-one.html
[6] awk实例一:简单入门
http://suo.iteye.com/blog/1319525
- PCIE的简单配置(r8笔记第82天)
- 7个深度神经网络可视化工具,不可错过!
- Pwnhub之奇妙的巨蟒 Writeup
- WINDOW 安装mysql5.7数据库,并设置密码及相关报错
- go channel 通信通道
- SQl 语句(常见) 新建,删除,修改表,新增字段,修改默认值
- SQL处理表结构的基本方法整理(创建表,关联表,复制表)
- Go web之旅(路由篇)
- Golang插入排序
- Golang写的并行排序算法
- Go中调用dll示例
- python 序列化数据:pickle与json ,dumps与loads
- golang继承,和多态
- python 利用random生成验证码与MD5码加密过程
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- 如何使用iostat查看linux硬盘IO性能
- linux搭建NFS文件共享服务器的步骤详解
- Windows和Linux实现远程桌面连接
- Ubuntu19.10开启ssh服务(详细过程)
- Ubuntu 18.04更换apt-get源的方法
- 浅谈Linux环境变量文件介绍
- linux创建线程之pthread_create的具体使用
- Ubuntu20.04安装搜狗输入法的详细步骤
- linux系统安装msf的过程详解
- Linux删除系统自带版本Python过程详解
- Linux时间子系统之时间的表示示例详解
- 如何在Linux中修改tomcat端口号
- centos7修改系统语言为简体中文的实现
- Linux 通过Rsync+Inotify实现本、异地远程数据实时同步功能
- linux实现猜数字小游戏源码