(41)不能对同一个关系(relation)进行JOIN
假设有如下文件:
1
2
3
4
5
|
[root@localhost ~]
# cat 1.txt
1 a
2 e
3
v
4 n
|
我想对第一列这样JOIN:
1
2
|
A =
LOAD
'1.txt'
AS
(col1:
int
, col2: chararray);
B =
JOIN
A
BY
col1, A
BY
col1;
|
那么当你试图 DUMP B 的时候,会报如下的错:
ERROR org.apache.pig.tools.grunt.Grunt - ERROR 1108: Duplicate schema alias: A::col1 in "B"
这是因为Pig会弄不清JOIN之后的字段名——两个字段均为A::col1,使得一个关系(relation)中出现了重复的名字,这是不允许的。
文章来源:http://www.codelast.com/
要解决这个问题,只需将数据LOAD两次,并且给它们起不同的名字就可以了:
01
02
03
04
05
06
07
08
09
10
|
grunt> A =
LOAD
'1.txt'
AS
(col1:
int
, col2: chararray);
grunt> B =
LOAD
'1.txt'
AS
(col1:
int
, col2: chararray);
grunt> C =
JOIN
A
BY
col1, B
BY
col1;
grunt> DESCRIBE C;
C: {A::col1:
int
,A::col2: chararray,B::col1:
int
,B::col2: chararray}
grunt> DUMP C;
(1,a,1,a)
(2,e,2,e)
(3,v,3,v)
(4,n,4,n)
|
从上面的 C 的schema,你可以看出来,如果对同一个关系A的第一列进行JOIN,会导致schema中出现相同的字段名,所以当然会出错。
(42)外部的JOIN(outer JOIN)
初次使用JOIN时,一般人使用的都是所谓的“内部的JOIN”(inner JOIN),也即类似于 C = JOIN A BY col1, B BY col2 这样的JOIN。Pig也支持“外部的JOIN”(outer JOIN),下面就举一个例子。
假设有文件:
1
2
3
4
5
|
[root@localhost ~]
# cat 1.txt
1 a
2 e
3
v
4 n
|
以及:
1
2
3
4
5
|
[root@localhost ~]
# cat 2.txt
9 a
2 e
3
v
0 n
|
现在来对这两个文件的第一列作一个outer JOIN:
01
02
03
04
05
06
07
08
09
10
|
grunt> A =
LOAD
'1.txt'
AS
(col1:
int
, col2: chararray);
grunt> B =
LOAD
'2.txt'
AS
(col1:
int
, col2: chararray);
grunt> C =
JOIN
A
BY
col1
LEFT
OUTER
, B
BY
col1;
grunt> DESCRIBE C;
C: {A::col1:
int
,A::col2: chararray,B::col1:
int
,B::col2: chararray}
grunt> DUMP C;
(1,a,,)
(2,e,2,e)
(3,v,3,v)
(4,n,,)
|
在outer JOIN中,“OUTER”关键字是可以省略的。从上面的结果,我们注意到:如果换成一个inner JOIN,则两个输入文件的第一、第四行都不会出现在结果中(因为它们的第一列不相同),而在LEFT OUTER JOIN中,文件1.txt的第一、四行却被输出了,所以这就是LEFT OUTER JOIN的特点:对左边的记录来说,即使它与右边的记录不匹配,它也会被包含在输出数据中。
文章来源:http://www.codelast.com/
同理可知RIGHT OUTER JOIN的功能——把上面的 LEFT 换成 RIGHT,结果如下:
1
2
3
4
|
(,,0,n)
(2,e,2,e)
(3,v,3,v)
(,,9,a)
|
可见,与左边的记录不匹配的右边的记录被保存了下来,而左边的记录没有保存下来(两个逗号表明其为空),这就是RIGHT OUTER JOIN的效果,与我们想像的一样。
有人会问,OUTER JOIN在实际中可以用来做什么?举一个例子:可以用来求“不在某数据集中的那些数据(即:不重合的数据)”。还是以上面的两个数据文件为例,现在我要求出 1.txt 中,第一列不在 2.txt 中的第一列的那些记录,肉眼一看就知道,1和4这两个数字在 2.txt 的第一列里没有出现,而2和3出现了,因此,我们要找的记录就是:
1
2
|
1 a
4 n
|
要实现这个效果,Pig代码及结果为:
01
02
03
04
05
06
07
08
09
10
|
grunt> A =
LOAD
'1.txt'
AS
(col1:
int
, col2: chararray);
grunt> B =
LOAD
'2.txt'
AS
(col1:
int
, col2: chararray);
grunt> C =
JOIN
A
BY
col1
LEFT
OUTER
, B
BY
col1;
grunt> DESCRIBE C;
C: {A::col1:
int
,A::col2: chararray,B::col1:
int
,B::col2: chararray}
grunt> D = FILTER C
BY
(B::col1
is
null
);
grunt> E = FOREACH D GENERATE A::col1
AS
col1, A::col2
AS
col2;
grunt> DUMP E;
(1,a)
(4,n)
|
可见,我们确实找出了“不重合的记录”。在作海量数据分析时,这种功能是极为有用的。
最后来一个总结:
假设有两个数据集(在1.txt和2.txt中),分别都只有1列,则如下代码:
1
2
3
4
5
6
|
A =
LOAD
'1.txt'
AS
(col1: chararray);
B =
LOAD
'2.txt'
AS
(col1: chararray);
C =
JOIN
A
BY
col1
LEFT
OUTER
, B
BY
col1;
D = FILTER C
BY
(B::col1
is
null
);
E = FOREACH D GENERATE A::col1
AS
col1;
DUMP E;
|
计算结果为:在A中,但不在B中的记录。
(43)JOIN的优化
请看这个链接:《Apache Pig中文教程(进阶)》
(44)GROUP时按所有字段分组可以用GROUP ALL吗
假设你有如下数据文件:
1
2
3
4
5
6
7
8
|
[root@localhost ~]
# cat 3.txt
1 9
2 2
3 3
4 0
1 9
1 9
4 0
|
现在要找出第1、2列的组合中,每一种的个数分别为多少,例如,(1,9)组合有3个,(4,0)组合有两个,依此类推。
显而易见,我们只需要用GROUP就可以轻易完成这个任务。于是写出如下代码:
1
2
3
4
|
A =
LOAD
'3.txt'
AS
(col1:
int
, col2:
int
);
B =
GROUP
A
ALL
;
C = FOREACH B GENERATE
group
,
COUNT
(A);
DUMP C;
|
可惜,结果不是我们想要的:
1
|
(all,7)
|
为什么呢?我们的本意是按所有列来GROUP,于是使用了GROUP ALL,但是这实际上变成了统计行数,下面的代码就是一段标准的统计数据行数的代码:
1
2
3
4
|
A =
LOAD
'3.txt'
AS
(col1:
int
, col2:
int
);
B =
GROUP
A
ALL
;
C = FOREACH B GENERATE
COUNT
(A);
DUMP C;
|
因此,上面的 C = FOREACH B GENERATE group, COUNT(A) 也无非就是多打印了一个group的名字(all)而已——group的名字被设置为“all”,这是Pig帮你做的。
文章来源:http://www.codelast.com/
正确的做法很简单,只需要按所有字段GROUP,就可以了:
1
2
3
4
|
A =
LOAD
'3.txt'
AS
(col1:
int
, col2:
int
);
B =
GROUP
A
BY
(col1, col2);
C = FOREACH B GENERATE
group
,
COUNT
(A);
DUMP C;
|
结果如下:
1
2
3
4
|
((1,9),3)
((2,2),1)
((3,3),1)
((4,0),2)
|
这与我们前面分析的正确结果是一样的。
(45)在Pig中使用中文字符串
有读者来信问我,如何在Pig中使用中文作为FILTER的条件?我做了如下测试,结论是可以使用中文。
数据文件 data.txt 内容为(每一列之间以TAB为分隔符):
1
2
3
4
5
|
1 北京市 a
2 上海市 b
3 北京市 c
4 北京市 f
5 天津市 e
|
Pig脚本文件 test.pig 内容为:
1
2
3
|
A =
LOAD
'data.txt'
AS
(col1:
int
, col2: chararray, col3: chararray);
B = FILTER A
BY
(col2 ==
'北京市'
);
DUMP B;
|
首先,我这两个文件的编码都是UTF-8(无BOM),在Linux命令行下,我直接以本地模式执行Pig脚本 test.pig:
1
|
pig -x local test.pig
|
得到的输出结果为:
1
2
3
|
(1,北京市,a)
(3,北京市,c)
(4,北京市,f)
|
可见结果是正确的。
文章来源:http://www.codelast.com/
但是,如果我在grunt交互模式下,把 test.pig 的内容粘贴进去执行,是得不到任何输出结果的:
1
2
3
|
grunt> A =
LOAD
'data.txt'
AS
(col1:
int
, col2: chararray, col3: chararray);
grunt> B = FILTER A
BY
(col2 ==
'北京市'
);
grunt> DUMP B;
|
具体原因我不清楚,但是至少有一点是肯定的:可以使用中文作为FILTER的条件,只要不在交互模式下执行你的Pig脚本即可。
(46)如何统计 tuple 中的 field 数,bag 中的 tuple 数,map 中的 key/value 组数
一句话:用Pig内建的 SIZE 函数:
Computes the number of elements based on any Pig data type.
具体可看这个链接。
(47)一个字符串为null,与它为空不一定等价
在某些情况下,要获取“不为空”的字符串,仅仅用 is not null 来判断是不够的,还应该加上 SIZE(field_name) > 0 的条件:
1
|
B = FILTER A
BY
(field_name
is
not
null
AND
(
SIZE
(field_name) > 0L));
|
注意,这只是在某些情况下需要这样做,在一般情况下,仅用 is not null 来过滤就可以了。我并没有总结出特殊情况是哪些情况,我只能说我我不是第一次遇到此情况了,所以才有了这一个结论。
注意上面使用的是“0L”,因为SIZE()返回的是long类型,如果不加L,在Pig0.10下会出现一个警告,例如:
[main] WARN org.apache.pig.PigServer - Encountered Warning IMPLICIT_CAST_TO_LONG 1 time(s)
(48)Pig中的各operator(操作符),哪些会触发reduce过程
①GROUP:由于GROUP操作会将所有具有相同key的记录收集到一起,所以数据如果正在map中处理的话,就会触发shuffle→reduce的过程。
②ORDER:由于需要将所有相等的记录收集到一起(才能 排序),所以ORDER会触发reduce过程。同时,除了你写的那个Pig job之外,Pig还会添加一个额外的M-R job到你的数据流程中,因为Pig需要对你的数据集做采样,以确定数据的分布情况,从而解决数据分布严重不均的情况下job效率过于低下的问题。
③DISTINCT:由于需要将记录收集到一起,才能确定它们是不是重复的,因此DISTINCT会触发reduce过程。当然,DISTINCT也会利用combiner在map阶段就把重复的记录移除。
④JOIN:JOIN用于求重合,由于求重合的时候,需要将具有相同key的记录收集到一起,因此,JOIN会触发reduce过程。
⑤LIMIT:由于需要将记录收集到一起,才能统计出它返回的条数,因此,LIMIT会触发reduce过程。
⑥COGROUP:与GROUP类似(参看本文前面的部分),因此它会触发reduce过程。
⑦CROSS:计算两个或多个关系的叉积。
(49)如何统计一个字符串中包含的指定字符数
这可以不算是个Pig的问题了,你可以把它认为是一个shell的问题。从本文前面部分我们已经知道,Pig中可以用 STREAM ... THROUGH 来调用shell进行辅助数据处理,所以在这我们也能这样干。
假设有文本文件:
1
2
3
4
|
123 abcdef:243789174
456 DFJKSDFJ:3646:555558888
789 yKDSF:00000%0999:2343324:11111:33333
|
现在要统计:每一行中,第二列里所包含的冒号(“:”)分别为多少?代码如下:
1
2
3
|
A =
LOAD
'1.txt'
AS
(col1: chararray, col2: chararray);
B = STREAM A THROUGH `awk -F
":"
'{print NF-1}'
`
AS
(colon_count:
int
);
DUMP B;
|
结果为:
1
2
3
|
(1)
(2)
(4)
|
文章来源:http://www.codelast.com/
(50)UDF是区分大小写的
因为UDF是由Java类来实现的,所以区分大小写,就这么简单。
(51)设置Pig job的job name
在Pig脚本开头加上一句:
1
|
set job.name 'My-Job-Name';
|
那么,执行该Pig脚本之后,在Hadoop的Job Tracker中看到的“Name”就是“My-Job-Name”了。
如果不设置,显示的name是类似于“Job6245768625829738970.jar”这样的东西,job多的时候完全没有标识度,建议一定要设置一个特殊的job name。
(52)把纯文本转化为JSON
假设输入文件 a.txt 内容为:
1
2
|
1 2
9 8
|
则如下Pig代码将把它转化为JSON格式:
1
2
|
A =
LOAD
'a.txt'
AS
(col1: chararray, col2: chararray);
B = STORE A
INTO
'result'
USING JsonStorage();
|
查看输出文件的内容是:
1
2
|
{"col1":"1","col2":"2"}
{"col1":"9","col2":"8"}
|
可见,你LOAD输入数据时定义的字段名,就是输出文件中的JSON字段名。