MapReduce实现两表的连接操作

MapReduce实现表连接

表一:

id	city
1   北京
2   天津
3   河北
4   山西
5   内蒙古
6   辽宁
7   吉林
8   黑龙江

表二:

id	year	num
1   2010    1962
1   2011    2019
2   2010    1299
2   2011    1355
4   2011    3574
4   2011    3593
9   2010    2303
9   2011    2347

需求

根据两张表的id,用mapreduce将两个表进行连接操作(关联)

1	北京	2011	2019
1	北京	2010	1962
2	天津	2011	1355
2	天津	2010	1299
4	山西	2011	3593
4	山西	2011	3574

思路:

map程序每次都是读取一行数据,读取两个表内的数据,可以根据数据来源(文件名称)判断当前读取的数据是来自哪一张表,然后打上标记送入reduce去处理,map输出的key是id值,value是id对应的数据。

reduce接收到的数据是<key,{value1,value2,value3,…}>,key是输入的id的值,value中包含表一的id对应的数据,也包含表二的id对应的数据,我们可以通过map里打的标记进行区分,分别记录到list1list2中,然后将两个list中的数据进行笛卡尔积就能得到一个id连接后的数据,将所有id都进行这样的操作,就能把整个表都处理完

代码

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;


public class ReduceJoinTest {

    //mapper类
    public static class mapper extends Mapper<LongWritable, Text, Text, Text> {
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            
            //下面两步能获取当前行数据的输入文件名称
            FileSplit fileSplit = (FileSplit) context.getInputSplit();
            String name = fileSplit.getPath().getName();

            //将当前行数据转换为标准的String
            String line = value.toString();
            //若数据无效则丢弃
            if (line == null || line.equals("")) return;
			
            //根据空格进行分割
            String[] split = line.split("\\s+");
            
            if (name.contains("tb_a")) {
                //如果当前行是表一,在city前添加一个标记“#”,以跟表二区分
                String id = split[0];
                String city = split[1];
                //输出key为id,value为city
                context.write(new Text(id), new Text("#" + city));
            } else if (name.contains("tb_b")) {
                //如果当前行是表二,在输出的value字段前添加“$”,以跟表一区分
                String id = split[0];
                String num1 = split[1];
                String num2 = split[2];
                //输出key为id,value为year与num
                context.write(new Text(id), new Text("$" + num1 + "\t" + num2));
            }
        }
    }

    //reducer类
    public static class reducer extends Reducer<Text, Text, Text, Text> {
        //输入的数据为<id,{value1,value2,....}>
        @Override
        protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
            //list1存表一带来的数据
            List<String> list1 = new LinkedList<>();
            //list2存表二带来的数据
            List<String> list2 = new LinkedList<>();
            
            //遍历values
            for (Text text : values) {
                String value = text.toString();
                //如果value数据以#开头,则为表一中的数据,添加至list1中
                if (value.startsWith("#")) {
                    value = value.substring(1);
                    list1.add(value);

                } else if (value.startsWith("$")) {
                    //如果value数据以$开头,则为表二中的数据,添加至list2中
                    value = value.substring(1);
                    list2.add(value);
                }
            }
            
            //将两表id相同的数据进行笛卡尔积,key为id,value为list1与list2的组合
            for (String a : list1) {
                for (String b : list2) {
                    context.write(key, new Text(a + "\t" + b));
                }
            }
        }
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

		//下面都是模板,只需修改输入与输出位置即可
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);
		
        job.setJarByClass(ReduceJoinTest.class);

        job.setMapperClass(mapper.class);
        job.setReducerClass(reducer.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        FileInputFormat.setInputPaths(job, new Path("L:\\JAVA\\Hadoop\\src\\xyz\\liujiawei\\join\\src\\input"));
        FileOutputFormat.setOutputPath(job, new Path("L:\\JAVA\\Hadoop\\src\\xyz\\liujiawei\\join\\src\\output"));

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

    }
}
  • 6
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
MapReduce实现两个连接可以通过以下步骤: 1. 在 Map 阶段,对两个待连接进行拆分,生成键值对,其中键为连接的关键字,值为待连接的记录。 2. 将生成的键值对进行分组,将具有相同键的记录放在同一个组中。 3. 在 Reduce 阶段,对于每个组,进行连接操作,将两个中符合条件的记录连接起来,生成新的记录。 下面是一个示例代码,假设有两个,一个是订单 orders,包含订单编号、客户编号和订单金额等字段;另一个是客户 customers,包含客户编号、客户姓名和联系方式等字段。现在需要按照客户编号连接这两个,生成客户订单,包含客户编号、客户姓名、联系方式和订单金额等字段。 Mapper 代码: ``` public class JoinMapper extends Mapper<LongWritable, Text, Text, Text> { public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] fields = line.split(","); if (fields.length == 3) { // customers String customerId = fields[0]; String customerName = fields[1]; String customerContact = fields[2]; context.write(new Text(customerId), new Text("c#" + customerName + "#" + customerContact)); } else if (fields.length == 4) { // orders String orderId = fields[0]; String customerId = fields[1]; String orderAmount = fields[2]; context.write(new Text(customerId), new Text("o#" + orderId + "#" + orderAmount)); } } } ``` Reducer 代码: ``` public class JoinReducer extends Reducer<Text, Text, Text, Text> { public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException { String customerName = null; String customerContact = null; List<String> orderIds = new ArrayList<>(); List<String> orderAmounts = new ArrayList<>(); for (Text value : values) { String[] fields = value.toString().split("#"); if (fields[0].equals("c")) { customerName = fields[1]; customerContact = fields[2]; } else if (fields[0].equals("o")) { orderIds.add(fields[1]); orderAmounts.add(fields[2]); } } if (customerName != null) { for (int i = 0; i < orderIds.size(); i++) { String orderId = orderIds.get(i); String orderAmount = orderAmounts.get(i); context.write(new Text(key + "," + customerName + "," + customerContact), new Text(orderId + "," + orderAmount)); } } } } ``` 以上代码中,Mapper 阶段将两个的记录拆分成键值对,其中键为客户编号,值为客户或订单记录,并通过前缀标识来区分是客户还是订单。在 Reduce 阶段,对于每个客户编号,将其对应的所有客户和订单记录进行连接操作,生成新的客户订单记录。最终输出的结果是以客户编号为键,客户姓名、联系方式、订单编号和订单金额为值的键值对。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值