分类: 云计算
2018-06-11 20:59:07
SQL join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
SQL join 有四种,以下person表和order表为例来说明
person 表
id_p | last_name | frist_name | address | city |
1 | Adams | John | Oxford Street | London |
2 | Bush | George | Fifth Avenue | New York |
3 | Carter | Thomas | Changan Street | Beijing |
order表
id_o | order_num | id_p |
1 | 7895 | 3 |
2 | 4467 | 3 |
3 | 2456 | 1 |
4 | 2456 | 1 |
5 | 4764 | 8 |
内连接也通常称为连接,如果表中有至少一个匹配,则返回行
SELECT a.last_name, a.frist_name, b.order_num FROM person a JOIN
orders b ON a.id_p=b.id_p;
last_name
frist_name
order_num
Carter
Thomas
7895
Carter
Thomas
4467
Adams
John
2456
Adams
John
2456
即使右表中没有匹配,也从左表返回所有的行
SELECT a.last_name, a.frist_name, b.order_num FROM person a
LEFT JOIN orders b ON a.id_p=b.id_p;
last_name | frist_name | order_num |
Adams | John | 2456 |
Adams | John | 2456 |
Bush | George | |
Carter | Thomas | 7895 |
Carter | Thomas | 4467 |
即使左表中没有匹配,也从右表返回所有的行
SELECT a.last_name, a.frist_name,
b.order_num FROM person a RIGHT JOIN
orders b ON a.id_p=b.id_p;
last_name
frist_name
order_num
Carter
Thomas
7895
Carter
Thomas
4467
Adams
John
2456
Adams
John
2456
\N
\N
4764
只要其中一个表中存在匹配,就返回行
SELECT a.last_name, a.frist_name, b.order_num FROM person a FULL JOIN orders b ON a.id_p=b.id_p;
last_name
frist_name
order_num
Carter
Thomas
7895
Carter
Thomas
4467
Adams
John
2456
Adams
John
2456
\N
\N
4764
Bush
George
\N
主要那个用于以下场景:两个待连接表中,有一个表非常大,而另一个表非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多份,让每个map task内存中存在一份(比如存放到hash table中),然后只扫描大表:对于大表中的每一条记录key/value,在hash table中查找是否有相同的key的记录,如果有,则连接后输出即可。
为了支持文件的复制,Hadoop提供了一个类DistributedCache,使用该类的方法如下:
(1)用户使用静态方法DistributedCache.addCacheFile()指定要复制的文件,它的参数是文件的URI(如果是HDFS上的文件,可以这样:hdfs://namenode:9000/home/XXX/file,其中9000是自己配置的NameNode端口号)。JobTracker在作业启动之前会获取这个URI列表,并将相应的文件拷贝到各个TaskTracker的本地磁盘上。
(2)用户使用DistributedCache.getLocalCacheFiles()方法获取文件目录,并使用标准的文件读写API读取相应的文件。
在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源的key/value数据对,对每条数据打一个标签(tag),比如:tag=0表示来自文件File1,tag=2表示来自文件File2。即:map阶段的主要任务是对不同文件中的数据打标签。
在reduce阶段,reduce函数获取key相同的来自File1和File2文件的value list, 然后对于同一个key,对File1和File2中的数据进行join(笛卡尔乘积)。即:reduce阶段进行实际的连接操作。
是从分布式数据库中借鉴过来的方法。它的产生动机是:对于在reduce中join,跨机器的数据传输量非常大,这成了join操作的一个瓶颈,如果能够在map端过滤掉不会参加join操作的数据,则可以大大节省网络IO。
实现方法很简单:选取一个小表,假设是File1,将其参与join的key抽取出来,保存到文件File3中,File3文件一般很小,可以放到内存中。在map阶段,使用DistributedCache将File3复制到各个TaskTracker上,然后将File2中不在File3中的key对应的记录过滤掉,剩下的reduce阶段的工作与在reduce 中join相同
在某些情况下,半连接抽取出来的小表的key集合在内存中仍然存放不下,这时候可以使用BloomFiler以节省空间。
BloomFilter最常见的作用是:判断某个元素是否在一个集合里面。它最重要的两个方法是:add() 和contains()。最大的特点是不会存在false negative,即:如果contains()返回false,则该元素一定不在集合中,但会存在一定的true negative,即:如果contains()返回true,则该元素可能在集合中。
因而可将小表中的key保存到BloomFilter中,在map阶段过滤大表,可能有一些不在小表中的记录没有过滤掉(但是在小表中的记录一定不会过滤掉),这没关系,只不过增加了少量的网络IO而已。