spark-使用DataFrameWriter或者spark-sql向hive分区表中插入数据

须知

1. toplink

2. saveAsTable是DataFrameWriter的方法,DFW会有mode和option,mode统一有4种,但saveAsTable没有option,可以在上面的官文中查看某方法有哪些option

3. saveAsTable执行后,原来hive的表的元数据会变,TBLPROPERTIES会增加很多spark相关的属性。但分区字段会变成普通字段,需要使用DataFrameWriter的partitionBy方法重新指定下分区。

4. 注意:sparksql中的INSERT OVERWRITE 后面必须加TABLE,否则报错

5. 注意:调整DF中的字段顺序、筛选字段,可以使用select方法

方案

1. saveAsTable

在这里插入图片描述

在此方法中,如果数据源表存在于Spark-catalog中,则使用save方法参数中的模式来确定如何插入。如果表不在Spark-catalog中,我们总是会覆盖它(比如JDBC数据源中的一个表),因为spark可以整合hive,所以hive中的表在Spark-catalog中,但比如spark创建的临时表,就不在Spark-catalog中。如果表在Spark-catalog中,则追加。
如果df根据一个没分区的 hive表创建,并且可以转换为hive内置的序列化器(比如ORC和Parquet),那么会以hive兼容的格式持久化到hdfs。否则,会以sparksql指定的格式持久化。

问题:
对象是分区表时,执行完分区表的元数据会变,分区字段变成普通字段,需要partitionBy方法指定分区字段
Overwrite模式时,会把其他分区覆盖,暂时没有好的方法

实验

创建一个分区表t1,然后在t1基础上添加一个字段,创建表t2
使用spark创建这个表的df1,然后添加字段转为df2,然后df2使用saveAsTable插入到t2的新分区。

环境

create table IF NOT EXISTS test1.parTable1(id string,name string) 
partitioned by (etl_date string) stored as parquet;
create table IF NOT EXISTS test1.parTable2(id string,name string,age string) 
partitioned by (etl_date string) stored as parquet;

1. saveAsTable

发现overwrite状态下会将所有分区覆盖,并且使用show create table会发现分区字段变成普通字段
在这里插入图片描述

df3.write.mode(SaveMode.Overwrite).format("parquet").saveAsTable("test1.parTable2")

添加.partitionBy(“etl_date”)方法后,分区字段还在,但还是会把其他所有分区删除

df3.write.partitionBy("etl_date").mode(SaveMode.Overwrite).format("parquet").saveAsTable("test1.parTable2")

网上没有找到解决方法,原因可能是因为表不是通过spark建的,所以会有问题,但也不能每次都通过spark建一次吧。网上说spark2.3已经解决这个问题。

2. insert into

不管有没有.partitionBy(“etl_date”),只要是Overwrite模式,都会覆盖所有分区。不加Overwrite默认append模式,不会删除其他分区但指定分区也不能覆盖

 df4.write
      .partitionBy("etl_date")
      .mode(SaveMode.Overwrite)
      .insertInto(tableName="test1.parTable2")

3. 注册为spark临时表然后直接调用hql,能解决分区的覆盖问题,但不能指定插入的字段。只能是拼接INSERT INTO后面的SELECT列表

    val code :(String => String) = (arg: String) => {"1"}
    val addCol = udf(code)
    val df3 = df2.withColumn("age",addCol(col("id")))
    val df4 = df3.select("id","name","age")
    df4.show()
   /*
   目的就是为了把df4插入到指定表中
    */
    df4.createOrReplaceTempView("temp_df4")
    sparkSession.sql(
      """
        | INSERT OVERWRITE TABLE test1.parTable2 partition(ETL_DATE='20200916')
        | SELECT * FROM temp_df4
      """.stripMargin)

拼接INSERT INTO后的SELECT 列表,没有的列设为null,因为select 后面如果不是列名而是值,则直接输出值。

val mxjgDF = sparkSession.table(s"ADS.${tableNamePre}_${modelName}_MXJG")
val fieldNames = mxjgDF.schema.fieldNames.dropRight(1) // 所有字段的按序数组
.map(_.toLowerCase)
Logl.error("CustomLog-MX-fieldNames:)
for (i <- fieldNames)
	logl.error(i)
}
Logl.error(s"CustomLog-MX-outputRule:outputRule")
val outputcols = outputRule.split(",")
	.map(s => if (s.startsWith("`") s.drop(1).dropRight(1) else s)
	.map(_.toLowerCase)
var colsStr = ""
for (i <- fieldNames){
	if (outputcois.contains(i)) colsStr += i + ","
	else colsstrHnuii)
colsstr = colsStr.dropRight(1)
Logl.error(s"CustomLog-MX-outputcols:${outputcols.foneach(printLn(_))}")
logl.error(s"CustomLog-MX-colsStr:$sStr")

val temp_outputDF:DataFrame = transfomer.transform(wideDF)
log1.error(s"预测结果DF:")
temp_outputDF.show(2)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值