saprkSQL的思考及开发实战(一)

sparkSQL:数据读入到sparkSQL,进行数处理或者算法实现,然后再把处理收的数据输出到相应的输出源中,在实际开发过程中需要考虑如下几个点:

1、数据源多少,sparkSql支持多种数据源hive\json\.txt\orc文件\jdbc;

2、字段的映射关系与语言的定义类型相关

3、数据拥有的情况下,组织这些数据->数据结构->操作数据

sparkSQL的实战介绍

2、hiveContext和hiveContext

sparkContext处理的是多种数据源,HiveContext处理的是hive源数据,它集成了sparkContext的两个方法;

analyze(tableName:String):对sql的解析分析

refreshTable(tableName:String):再sparkSQL中处理的某个便的存储为止发生了变化

需要再内存metaDate中缓存这张表,需要调用这个方法来使这个缓存无效重新加载。

3.1读取数据

json文件.txt文件person[]

{"id":1,"name":"bob","total":10}

{"id":2,"name":"alice","total":15}

{"id":3,"name":"alice","total":40}

{"id":4,"name":"jerry","total":25}

Json文件personloan发放额【total】

{"id":1,"name":"bob","total":40}

{"id":2,"name":"alice","total":65}

{"id":3,"name":"alice","total":20}

{"id":4,"name":"jerry","total":25}

 

eg:

val sql=new SQLContext(sc) //声明一个sqlContext的对象,以便对数据进行操作

val shangpin=sql.read.json("文件路径")

//shangpin返回结果是org.apache.spark.sql.DataFrame=

[total:bigint,id:bigint,name:String],就这样把数据读入到内存中了

描述:找到文件,以json的方式进行解析,同时通过json的key形成schema,顺序不一定,

int->bigint;""->String,返回结果是一个DataFrame数据类型;

DataFrame是按照行进行存储的Row,一行一行进行呼叫存储,处理粒度是行粒度,数据结构读入sparkSql中的数据,

DataFrame数据结构和数据库的结构有相似之处,数据按照行进行存储,

同时还有一个schema,相当于数据库的表结果,记录数据属于那个字段;

shangpin.schema;//查看schema的数据

返回结果:org.apache.spark.sql.types.structType=StructType(StructField(total,LongType,true),StructField(id,LongType,true)StructField(name,StringType,true))

从上看出shangpin的存储数据信息,表与scala的对应的关系:bigint->Long;int->Int;

Float->Float,double->Double,string->String等;DataFrame有scheam和行存储的数据;

read的其他接口

json(path:String):读取json文件

table(tableName:String)读取数据库中的表

jdbc:(url:String,table:String,predicates:Array[String],connectionProperties:Properties):通过jdbc连接数据库

orc(path:String):读取orc格式进行存储的文件

parquet(path:String):读取parquet格式进行存储的文件

schema(schema:StructType):读入数据指定了schema,底层不会再次解析schema从而进行了优化,

一般不需要这样的优化。

3.2数据读入:

处理从一个数据表中选择部分字段,而不是选择表中的所有字段,可以采用这个数据读入方式比较有优势

val shangpin=sql.sql("""select * from shangpin""".stripMargin)

//stripMargin方法用来解析sql

 

3.3数据写入,一般都是dataFrame类,不是这个类型进行组装

通过类进行构建:

val  sql=new SQLContext(sc)//创建一个sqlContext对象

import sql.implicits._//z这个是我们上面定义的sqlContxt

val peoson=sc.textFile(person.txt)//采用spark类型读入数据,SQLContext会带有schema

case clase person(Id:Int,name:String,total:Int)//定义类

valu personInfo=person.map(lines=>lines.split(",")).map(p=>person(p(0).toInt,p(1),p(2).toInt)).toDF

//构建成了 dataFrame,需要导入sql.implicys._,使用反射生成一个DataFrame

直接构造schema,需要把数据转化为row类型

val sql=new SQLContext(sc);

val person=sc.textFile("person.txt").map(lines=>lnes.split(","))//=>匿名函数

val personRow=sc.map(p=>Row(p(0),p(1),p(2)))//rdd转换成RDD(Row)类型

val schema=StructType(StructFile("id",IntegerType,true)::

                                         StructFile("nme",StringtType,true)::

                                          StructFile("total",IntegerType,true)::Nil)

val personInfo=sql.createDataFrame(peopleRow,schema)

//保证长度与类型一致structFile(字段名称,类型,数据是否用null进行填充)

直接构造的制约受字段的多少限制

写数据操作:

val sql=new SQLContext(sc);

val person=sc.textFile("person.txt").map(lines=>lnes.split(","))//=>匿名函数

val personRow=sc.map(p=>Row(p(0),p(1),p(2)))//rdd转换成RDD(Row)类型

val schema=StructType(StructFile("id",IntegerType,true)::

                                         StructFile("nme",StringtType,true)::

                                          StructFile("total",IntegerType,true)::Nil)

val personInfo=sql.createDataFrame(peopleRow,schema)

personInfo.registerTempTable("tempTable")//中间注册表

sql.sql("""insert overwrite table tagetTable select * from tempTable """.stripMargin)

sql()就是用来执行sql的语句

3.4通过DataFrame中方法对数据进行操作

1>对schema的操作

val sql=new SQLContext(sc);

val person=sql.read.json("person.txt")//person是一个DataFrame类型对象

person.schema//返回类型

person.dtypes//以数组额形式分会schema,Array[(String, String)] = Array((age,LongType), (id,LongType), (name,StringType))

person.columns//返回schema的字段

person.printSchema//以tree的形式打印输出schema

2>对表的操作,sql()方法调用sql,更灵活和实用:

1.方法(sql=new SQLContext(sc))df是DataFrame对象:实例说明

2.sql.read.table(tableName):读取一张表的数据

3.df.where(),df.filter():过滤条件,相当于sql的where部分,用法:选择总额中大于20的字段

返回值类型:DataFrame,df.where("total>=20"),df.filter("total>=20")

4.df.limit():限制输出的行数,对应sql的limit,用法:限制输出一百行,返回值类型DataFrame,df.limit(100)

5.df.join:链接操作,相当于sql的join

6.df.groupBy():聚合操作,相当于sql的groupBy,用法:对于某几行进行聚合

返回值类型:DataFrame

7.df.agg():求聚合函数

8.df.intersect(other:DataFame):求两个DataFrame的交集

9.df.exccept(other:DataFrame):求df中而不再other的行

10.df.withColumn(colName:String,col:Column)增加一列

11.df.withColumnRenamed(exName,newName)对某一列的名字进行重新命名

12.df.map();df.flatMap();df.mapPartitions();df.foreach();df.foreachPartition();df.collect();df.collectAsList();

df.repartition();df.distinct();df.count():这些方法都是对spark的rdd的操作,其中再DataFrame类中也封装了这些方法,需要注意的是这谢谢方法的放回

值类型是RDD的类型

13.df.select:选取某几列元素,这个方法相当于sql的select功能,返回类型是DataFrame

eg:df.seletct("id","name")

3.5sparkSQL的join操作

1、内连接,等值链接,会把链接的列和步兵成一个列

eg:val sql=new SQLContext(sc);

val personInfo=sql.read.json("person.txt");

val personloan=sql.read.json("loan.txt")

val info_loan=personInfo.join(personloan,"id")//通过id进行内连接

val info_loan0=personInfo.join(personloan,Seq("id","name"))

/*join 还支持左连接和右连接,其含义和sql中的差不多*/

eg:

//单字段链接

val left=personInfo.join(personloan,personInfo("id")===personloan("id"),"left_outer")

//多字段链接

val left0=personInfo.join(personloan,personInfo("id")===psersonloan("id") and personInfo("name")===personloan("name"),"left_outer")

由上面的执行结果知道sql中的缺陷存在会出现重复的字段名称,利用registerTempTable()函数把此DataFrame注册成一个内部表,然后通过sql.sql("")写sql的方法进行链接,

解决重复字段。

eg:

object RDD2DataFrame {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setMaster("local").setAppName("RDD2DataFrame ");

    val sc = new SparkContext(conf);

    val sqlContext = new SQLContext(sc);

    /**

      * 在scala中使用反射方法,进行RDD到DataFrame的转换,需要手动导入一个隐式转换

      */

    import sqlContext.implicits._

    case class Personloan(id:Int,name:String,total:Int)

    //这里其实就是一个普通的,元素为case class的RDD

    //直接使用RDD 的toDF,即可将其转换为DataFrame

    val personloanDF = sc.textFile("./student.txt", 1)

      .map(line => line.split(","))

      .map(arr => Personloan(arr(0).trim().toInt,arr(1),arr(2).trim().toInt))

      .toDF();//不知道为什么此处总是报错,无法继续运行

    personloanDF .registerTempTable("spersonloan ");//用dataframe注册一个临时表

    val PerloanDF = sqlContext.sql("select * from personloan where total<=20");   

   val PerloanRDD = tPerloanDF.rdd

    PerloanRDD.map(row => Personloan(row(0).toString().toInt,row(1).toString,row(2).toString.toInt))

      .collect()

      .foreach(per => println(per.id +":" + per.name + ": " + per.age))

  }

}

3.6sparkSQL操作agg

一般使用agg和groupby()一起操作

val personloan=new SQLContext(sc).read.json("personloan.txt")

val agg1=personloan.groupBy("name").agg("salary"->"avg")

val agg2=personloan.groupby("id","name").agg("salary"->"avg")

val agg3=personloan.groupby("name").agg(Map("id"->"avg","salary"->"max"))

使用agg操作时候,同一个字段只能进行一次操作,接入类型是Map,key-vlue的结构,当key相同时候,会覆盖原来的

3.7sqprksql的na操作

....

    Dataset<Row> nums =

    spark.createDataset(rowList, RowEncoder.apply(DataTypes.createStructType

     (new StructField[]{

                DataTypes.createStructField("col_1", DataTypes.IntegerType, true),

                DataTypes.createStructField("col_2", DataTypes.IntegerType, true),

        })));

        nums.show(false);

        DataFrameNaFunctions dataFrameNaFunctions = nums.na();

        dataFrameNaFunctions.drop("any").show();

        // 当某行中的所有列为null或NaN时丢弃掉此行

        dataFrameNaFunctions.drop("all").show();

        // 当某行的指定列为null或any时丢弃掉此行

        dataFrameNaFunctions.drop(new String[]{"col_1", "col_2"}).show();

....

主要的方法由drop();fill();replace()

 以上是实际开发过程中的一些汇总和整理,欢迎大家及时交流

补充:去重字段,还可以使用select指定字段或者drop方法删除重复字段或者seq对象实现字段的去重操作

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值