GROUP BY
array index 를 활용하면 group by 연산을 할 수 있습니다.
왜 그렇게 되는지 살펴보면 array a
의 index 값 $1 이 BBB 일 경우 a[BBB] 가 되는데
이때는 index 값으로 BBB 를 가지는 array 원소가 하나 생기는 것입니다.
value 값을 대입하지 않았기 때문에 value 는 null 이 되구요.
다음에 $1 값으로 BBB 가 다시 나타나면 a[BBB] 가 되어
index 값이 중복되므로 결과적으로 하나만 남게 됩니다.
두 번째 예제에서처럼 a[$1]++
을 하게 되면 이때는 a[BBB]++ 가되어
처음에 a[BBB] 원소가 생성됨과 동시에 value 가 1 이 됩니다.
다음에 $1 값으로 BBB 가 중복되어 나타나면 다시 a[BBB]++ 가 되므로
a[BBB] 의 value 가 +1 되어 2 가 됩니다.
$ cat file
AAA
EEE
BBB
CCC
DDD
DDD
BBB
BBB
...................................................
# select $1 from file group by $1
$ awk '{ a[$1] } END { for (i in a) print i }' file
AAA
CCC
EEE
BBB
DDD
# select $1, count($1) from file group by $1
$ awk '{ a[$1]++ } END { for (i in a) print i " : " a[i] }' file
AAA : 1
CCC : 1
EEE : 1
BBB : 3
DDD : 2
# select distinct($1) from file
$ awk '!a[$1]++' file
AAA
EEE
BBB
CCC
DDD
# select $1, count($1) as c from file group by $1 having c >= 2
$ awk '++a[$1] == 2' file # 중복라인을 가지고 있는 라인
DDD
BBB
낱말 출현 빈도 계산 후 정렬하기
# '.' 와 ',' 문자가 낱말에 포함되는 것을 방지하기 위해 RS 로 설정
$ awk -f - <<\EOF RS='\\.|,' song.txt
{
for (i=1; i<=NF; i++)
words[$i]++
}
END {
PROCINFO["sorted_in"] = "compare"
for (i in words)
printf "%s %s\n", i, words[i]
}
# 이 비교 함수는 awk 명령 출력을 "| LC_ALL=C sort -k2,2r -k1,1" 한 것과 같습니다.
# ORDER BY count DESC, word ASC
function compare(i1, v1, i2, v2)
{
if (v1 > v2) return -1 # 출현 빈도를 DESC 정렬하는 부분
else if (v1 == v2)
{ # { ... } 부분이 낱말을 ASC 정렬하는 부분
if ( i1 < i2 ) return -1
else if ( i1 == i2 ) return 0
else return 1
}
else return 1
}
EOF
< song.txt 파일 내용 >
길이 4
대한 4 동해물과 백두산이 마르고 닳도록
대한으로 4 하느님이 보우하사 우리나라 만세.
무궁화 4 무궁화 삼천리 화려강산
보전하세 4 대한 사람, 대한으로 길이 보전하세.
사람 4
삼천리 4 남산 위에 저 소나무, 철갑을 두른 듯
화려강산 4 바람서리 불변함은 우리 기상일세.
우리 2 무궁화 삼천리 화려강산
이 2 대한 사람, 대한으로 길이 보전하세.
가슴 1
가을 1 가을 하늘 공활한데 높고 구름 없이
공활한데 1 밝은 달은 우리 가슴 일편단심일세.
괴로우나 1 무궁화 삼천리 화려강산
구름 1 대한 사람, 대한으로 길이 보전하세.
기상과 1
기상일세 1 이 기상과 이 맘으로 충성을 다하여
나라 1 괴로우나 즐거우나 나라 사랑하세.
..... 무궁화 삼천리 화려강산
..... 대한 사람, 대한으로 길이 보전하세.
현재 각 cpu core 에 바운드된 스레드 개수 출력
# ps 명령에서 psr 은 현재 스레드가 바운드된 cpu core 를 말하고
# 뒤에 '=' 는 ps 명령이 타이틀을 표시하지 않게 합니다.
# 출력 결과가 실제 코어 수 보다 많게 나오는 이유는 Hyper-Threading 때문입니다.
$ watch -td -n1 "$( cat <<\EOF
ps axH -o psr= |
awk '{a[$1]++} END {
for (i in a) { printf "cpu%-6d",i }; print "";
for (i in a) { printf "%-9d", a[i] }; print "" }'
EOF
)"
cpu0 cpu1 cpu2 cpu3 cpu4 cpu5 cpu6 cpu7
289 232 269 198 260 214 279 215
group by 로 두 개 이상의 값을 사용
SUBSEP 을 사용하는 arr[$1,$3,$5]
형태를 이용하면 group by 로 두 개 이상의 값을 사용할 수 있습니다.
아래의 경우는 A, B 두 값을 group by 하기 위해 a[$1,$2]
를 사용하였습니다.
select 절에 count(C)
가 사용되었다면 입력 때는 a[$1,$2]["C"]++
, 출력 때는 a[$1,$2]["C"]
을 사용하면 되는데요.
count(distinct C)
가 사용되었기 때문에 distinct 를 구현하기 위해 입력 때는 a[$1,$2]["C"][$3]
, 출력 때는 length(a[$1,$2]["C"])
가 사용되었습니다.
만약에 count(distinct C,D)
를 한다고 하면 입력 때 a[$1,$2]["C"][$3,$4]
로 하면 되겠죠
# awk 에서 데이터 파일을 사용할 때는 헤더 부분은 제외하세요
###################### 데이터 파일 내용 #######################
A B C D E
---------------------- # x x 와 z o 는 2번씩 중복되고 x x 는 C 값이 같고 z o 는 다르다
x x e 2 10 # x x e
y y g 1 8
z o e 2 9 # z o e
o o q 1 10
p z e 3 22
x x e 1 11 # x x e
z o a 1 24 # z o a
y z b 1 25
#################### SQL 문으로 나타낸 결과 ####################
select A,
B,
count(distinct C),
sum(D),
sum(case when E>20 then E else 0 END)
from test
group by A,B
A B count(distinct C) sum(D) sum(case when E>20 then E else 0 END)
-------------------------------------------------------
o o 1 1 0
p z 1 3 22
x x 1 3 0
y y 1 1 0
y z 1 1 25
z o 2 3 24
################################ AWK ####################################
$ awk -f - <<\EOF OFS='\t' file
{ a[$1,$2]["C"][$3]; a[$1,$2]["D"] += $4; a[$1,$2]["E"] += ($5>20 ? $5:0) }
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( i in a ) {
split(i, b, SUBSEP)
print b[1], b[2], length(a[i]["C"]), a[i]["D"], a[i]["E"]
}
}
EOF
o o 1 1 0
p z 1 3 22
x x 1 3 0
y y 1 1 0
y z 1 1 25
z o 2 3 24
ORDER BY
array index 로 a[$1,$2]
가 사용되었기 때문에 order by A, B
를 하기 위해서는
간단히 awk 에서 기본적으로 제공하는 @ind_str_asc
을 사용하면 되었는데요.
만약에 order by E
를 하려면 어떻게 해야 될까요?
이렇게 다차원 배열이 사용되는 환경에서는 value 에 적용되는 @val_num_asc
을 사용할 수 없습니다.
이때는 사용자 정의 비교 함수를 이용해 order by 절을 구현해야 합니다.
출력에 for ( i in a )
문을 사용하므로 비교 함수에 전달되는 인수 값은
index 에 해당하는 i1, i2 값이 i
와 같게 되고 value 에 해당하는 v1, v2 값은 a[i]
와 같게됩니다.
select A,
B,
count(distinct C),
sum(D),
sum(case when E>20 then E else 0 END) as E
from test
group by A,B
order by E
.................................................
$ awk -f - <<\EOF OFS='\t' file
{ a[$1,$2]["C"][$3]; a[$1,$2]["D"] += $4; a[$1,$2]["E"] += ($5>20 ? $5:0) }
END {
PROCINFO["sorted_in"] = "compare"
for ( i in a ) {
split(i, b, SUBSEP)
print b[1], b[2], length(a[i]["C"]), a[i]["D"], a[i]["E"]
}
}
function compare(i1, v1, i2, v2)
{
if ( v1["E"] < v2["E"] ) return -1
else if ( v1["E"] == v2["E"] ) return 0
else return 1
}
EOF
y y 1 1 0
o o 1 1 0
x x 1 3 0
p z 1 3 22
z o 2 3 24
y z 1 1 25
HAVING
having
절을 구현하는 것은 비교적 간단합니다.
아래와 같이 if 문을 하나 추가하면 됩니다.
select A,
B,
count(distinct C),
sum(D) as D,
sum(case when E>20 then E else 0 END) as E
from test
group by A,B
having D > 1
order by E
.................................................
$ awk -f - <<\EOF OFS='\t' file
{ a[$1,$2]["C"][$3]; a[$1,$2]["D"] += $4; a[$1,$2]["E"] += ($5>20 ? $5:0) }
END {
PROCINFO["sorted_in"] = "compare"
for ( i in a ) {
if ( a[i]["D"] > 1 ) { # having 절을 위해 추가된 if 문
split(i, b, SUBSEP)
print b[1], b[2], length(a[i]["C"]), a[i]["D"], a[i]["E"]
}
}
}
function compare(i1, v1, i2, v2)
{
if ( v1["E"] < v2["E"] ) return -1
else if ( v1["E"] == v2["E"] ) return 0
else return 1
}
EOF
x x 1 3 0
p z 1 3 22
z o 2 3 24
UNION
UNION 은 order1 과 order2 의 항목들을 하나로 합칩니다.
이때 공통으로 존재하는 banana 와 strawberry 는 중복이 제거됩니다.
다음은 union 결과에 대해 order1 과 order2 에 존재 여부를 "O"
, "X"
로 표시합니다.
$ cat order1 $ cat order2
apple melon
banana cherry
grape banana
strawberry strawberry
orange mango
tomato
-------------------------------------------------
$ awk -f - <<\EOF order1 order2
BEGIN {
while (getline < ARGV[1] > 0) { u[$1]; o1[$1]=1 } # u[$1] 는 union 을 위한 array
while (getline < ARGV[2] > 0) { u[$1]; o2[$1]=1 }
} END {
printf "%12s | %s | %s |\n", " ", ARGV[1], ARGV[2]
print "--------------------------------"
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( i in u )
printf "%12s | %s | %s |\n" ,
i , ( o1[i] ? "O" : "X" ) , ( o2[i] ? "O" : "X" )
}
EOF
| order1 | order2 |
--------------------------------
apple | O | X |
banana | O | O |
cherry | X | O |
grape | O | X |
mango | X | O |
melon | X | O |
orange | O | X |
strawberry | O | O |
tomato | X | O |