单表链接与多表链接

一、多表链接

1、功能

    单表关联要求从给出的数据中寻找所关心的数据,是对原始数据所包含信息的挖掘,多表(逻辑)关联和单表关联类似,它也是通过对原始数据进行一定的处理,从其中挖掘出关心的信息。

2、设计

    多表关联和单表关联相似,都类似于数据库中的自然连接。相比单表关联,多表关联的左右表和连接列更加清楚。所以可以采用和单表关联的相同的处理方式,map识别出输入的行属于哪个表之后,对其进行分割,将连接的列值保存在key中,另一列和左右表标识保存在value中,然后输出。reduce拿到连接结果之后,解析value内容,根据标志将左右表内容分开存放,然后求笛卡尔积,最后直接输出

3、代码分析

package com.chinasofti.hadooptest;

import java.io.IOException;

import java.util.Iterator;

import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

public class MTJoin {

public static int time = 0;

/**

 * 在map中先区分输入行属于左表还是右表,然后对两列值进行分割, 保存连接列在key值,剩余列和左右表标志在value中,最后输出

 */

public static class Map extends Mapper<Object, Text, Text, Text> {

// 实现map函数

public void map(Object key, Text value, Context context)

throws IOException, InterruptedException {

String line = value.toString();// 每行文件

String relationtype = "";// 左右表标识

// 输入文件首行,不处理

if (line.contains("factoryname") || line.contains("addressed")) {

return;

}

// 输入的一行预处理文本

//根基第一个数据是否是数字判定当前数据是左表逻辑还是右表逻辑

String[] datas = line.split(" ");

String mapkey = datas[0].charAt(0) >= '0'&& datas[0].charAt(0) <= '9' ? datas[0] : datas[1];

String mapvalue = datas[0].charAt(0) >= '0' && datas[0].charAt(0) <= '9' ? datas[1] : datas[0];

relationtype = datas[0].charAt(0) >= '0'&& datas[0].charAt(0) <= '9' ? "2" : "1";

// 输出左右表

context.write(new Text(mapkey), new Text(relationtype + "+"

+ mapvalue));

}

}

/**

 * reduce解析map输出,将value中数据按照左右表分别保存,然后求出笛卡尔积,并输出。

 */

public static class Reduce extends Reducer<Text, Text, Text, Text> {

// 实现reduce函数

public void reduce(Text key, Iterable<Text> values, Context context)

throws IOException, InterruptedException {

// 输出表头

if (0 == time) {

context.write(new Text("factoryname"), new Text("addressname"));

time++;

}

int factorynum = 0, addressnum = 0;

String[] factory = new String[10], address = new String[10];

Iterator ite = values.iterator();

while (ite.hasNext()) {

String record = ite.next().toString();

int i = 2;

if (0 ==  record.length()) {

continue;

}

// 取得左右表标识

char relationtype = record.charAt(0);

// 左表

if ('1' == relationtype) {

factory[factorynum] = record.substring(i);

factorynum++;

}

// 右表

if ('2' == relationtype) {

address[addressnum] = record.substring(i);

addressnum++;

}

}

// 求笛卡尔积

//通过笛卡尔积获取结果,由语句"0 != factorynum && 0 != addressnum"得知,只要在"value-list"中没有左表或者右表,则不会做处理

可以根据这条规则去除无效的shuffle连接。

if (0 != factorynum && 0 != addressnum) {

for (int m = 0; m < factorynum; m++) {

for (int n = 0; n < addressnum; n++) {// 输出结果

context.write(new Text(factory[m]), new Text(address[n]));

}

}

}

}

}

public static void main(String[] args) throws Exception {

Configuration conf = new Configuration();

String[] otherArgs = new GenericOptionsParser(conf, args)

.getRemainingArgs();

if (otherArgs.length != 2) {

System.err.println("Usage: wordcount <in> <out>");

System.exit(2);

}

Job job = new Job(conf, "word count");

job.setJarByClass(MTJoin.class);

job.setMapperClass(Map.class);

job.setCombinerClass(Reduce.class);

job.setReducerClass(Reduce.class);

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(IntWritable.class);

FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

System.exit(job.waitForCompletion(true) ? 0 : 1);

}

}



二、单表链接

1、功能

"单表关联"这个实例要求从给出的数据中寻找所关心的数据,它是对原始数据所包含信息的挖掘。

2、设计

    实例中给出child-parent(孩子——父母)映射表,要求输出grandchild-grandparent(孙子——爷奶)映射表。分析这个实例,显然需要进行类似RDBMS中的单表自连接操作,连接的是同一个表中左表(逻辑)的parent列和右表(逻辑)的child列。连接结果中除去连接的两列就是所需要的结果——"grandchild--grandparent"表。要用MapReduce解决这个实例,首先应该考虑如何将数据看作表,并实现表的自连接;其次就是连接列的设置;最后是结果的整理。考虑到MapReduce的shuffle过程会将相同的key会连接在一起,所以可以将map结果的key设置成待连接的列,然后列中相同的值就自然会连接在一起了。要连接的是左表的parent列和右表的child列,且左表和右表是同一个表,所以在map阶段将读入数据分割成child和parent之后,会将parent设置成key,child设置成value进行输出,并作为左表;再将同一对child和parent中的child设置成key,parent设置成value进行输出,作为右表。为此需要在输出的value中再加上左右表的信息,比如在value的String最开始处加上字符1表示左表,字符2表示右表。这样在map的结果中就形成了左表和右表,然后在shuffle过程中完成连接。reduce接收到连接的结果,其中每个key的value-list就包含了"grandchild--grandparent"关系。取出每个key的value-list进行解析,将左表中的child放入一个数组,右表中的parent放入一个数组,然后对两个数组求笛卡尔积就是最后的结果了。

3、代码分析

package com.chinasofti.hadooptest;

import java.io.IOException;

import java.util.Iterator;

import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

public class STJoin {

public static int time = 0;//定义全局变量用于输出结果文件的表头

/* *

 * map将输出分割child和parent,然后正序输出一次作为右表,

 * 反序输出一次作为左表,需要注意的是在输出的value中必须加上左右表的区别标识。

 */

public static class Map extends Mapper<Object, Text, Text, Text> {

// 实现map函数

public void map(Object key, Text value, Context context)

throws IOException, InterruptedException {

String childname = "";// 孩子名称

String parentname = "";// 父母名称

String relationtype = "";// 左右表标识

// 输入的一行预处理文本

String[] values = value.toString().split(" ");//对输入的数据进行分割。分割成为孩子和父母名字两部分

if (!values[0].equals("child")) {// 忽略表头。忽略首行无用数据

childname = values[0];

parentname = values[1];//分别获取孩子和父母姓名

// 输出左表

relationtype = "1";

context.write(new Text(values[1]), new Text(relationtype + "+"

+ childname + "+" + parentname));

// 输出右表

relationtype = "2";

context.write(new Text(values[0]), new Text(relationtype + "+"

+ childname + "+" + parentname));

}

}

}

public static class Reduce extends Reducer<Text, Text, Text, Text> {

// 实现reduce函数

public void reduce(Text key, Iterable<Text> values, Context context)

throws IOException, InterruptedException {

if (0 == time) {// 在Reduce中输出表头,如果是第一次进行reduce操作,则输出表头,并改变全局变量值,下次执行时不再输出表头信息

context.write(new Text("grandchild"), new Text("grandparent"));

time++;

}

int grandchildnum = 0, grandparentnum = 0;

String[] grandchild = new String[10], grandparent = new String[10];

Iterator ite = values.iterator();

while (ite.hasNext()) {

String record = ite.next().toString();

if (0 == record.length()) {

continue;

}

//获取每个值中的父辈名称和孩子名称

String[] datas = record.split("\\+");

char relationtype = datas[0].charAt(0); // 取得左右表标识

String childname = "", parentname = ""; // 定义孩子和父母变量

childname = datas[1];// 获取value-list中value的child

parentname = datas[2];// 获取value-list中value的parent

//通过表示判定是逻辑左表还是逻辑右表

if ('1' == relationtype) { // 左表,取出child放入grandchildren

grandchild[grandchildnum++] = childname;

}

if ('2' == relationtype) {// 右表,取出parent放入grandparent

grandparent[grandparentnum++] = parentname;

}

}

// grandchild和grandparent数组求笛卡尔儿积,通过笛卡尔积获取结果

if (0 != grandchildnum && 0 != grandparentnum) {

//由语句"0 != grandchildnum && 0 != grandparentnum"得知,只要在"value-list"中没有左表或者右表,则不会做处理

可以根据这条规则去除无效的shuffle连接。

 

for (int m = 0; m < grandchildnum; m++) {

for (int n = 0; n < grandparentnum; n++) {

context.write(new Text(grandchild[m]), new Text(

grandparent[n]));

}

}

}

}

}

public static void main(String[] args) throws Exception {

Configuration conf = new Configuration();

String[] otherArgs = new GenericOptionsParser(conf, args)

.getRemainingArgs();

if (otherArgs.length != 2) {

System.err.println("Usage: wordcount <in> <out>");

System.exit(2);

}

Job job = new Job(conf, "word count");

job.setJarByClass(STJoin.class);

job.setMapperClass(Map.class);

job.setCombinerClass(Reduce.class);

job.setReducerClass(Reduce.class);

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(IntWritable.class);

FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

System.exit(job.waitForCompletion(true) ? 0 : 1);

}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值