1. 程式人生 > 其它 >常用統計分析 SQL 在 AWK 中的實現

常用統計分析 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

http://bit.ly/291uE5V

[5] AWK程式設計語言

http://awk.readthedocs.io/en/latest/chapter-one.html

[6] awk例項一:簡單入門

http://suo.iteye.com/blog/1319525