rdd dataframe dataset互转
DataFrame/Dataset转RDD:
val rdd1=testDF.rdd
val rdd2=testDS.rdd
RDD转DataFrame:
import spark.implicits._
val testDF = rdd.map {line=>
(line._1,line._2)
}.toDF("col1","col2")
Dataset转DataFrame:
import spark.implicits._
val testDF = testDS.toDF
DataFrame转Dataset:
import spark.implicits._
case class Coltest(col1:String,col2:Int)extends Serializable //定义字段名和类型
val testDS = testDF.as[Coltest]
读取数据库
object DataFrameOperations {
def main (args: Array[String ]) {
val sparkConf = new SparkConf().setAppName( "Spark SQL DataFrame Operations").setMaster( "local[2]" )
val sparkContext = new SparkContext(sparkConf)
val sqlContext = new SQLContext(sparkContext)
val url = "jdbc:mysql://m000:3306/test"
val jdbcDF = sqlContext.read.format( "jdbc" ).options(
Map( "url" -> url,
"user" -> "root",
"password" -> "root",
"dbtable" -> "spark_sql_test" )).load()
val joinDF1 = sqlContext.read.format( "jdbc" ).options(
Map("url" -> url ,
"user" -> "root",
"password" -> "root",
"dbtable" -> "spark_sql_join1" )).load()
val joinDF2 = sqlContext.read.format( "jdbc" ).options(
Map ( "url" -> url ,
"user" -> "root",
"password" -> "root",
"dbtable" -> "spark_sql_join2" )).load()
... ...
}
}
几个选项说明一下:
- url - 数据库的 JDBC 连接串
- dbtable - 可以是表名,也可以是一个子查询。如果是子查询的话,必须用括号括起来,并加别名,参见上面代码示例。
- user - 数据库用户名
- password - 数据库密码
- partitionColumn - 用于并发分区的表字段。下面几个选项都是围绕这个字段来的。Spark 会根据分区数量按这个字段的上下限把取出来的数据等分成几份,并行处理。
- lowerBound - 字段下限
- upperBound - 字段上限
- numPartitions - 分区数量
得到的 DataFrame,可以通过字段名来引用列或者某行的值,如:
读写
val parquetFile = sqlContext.read.parquet("hdfs:///path/to/hdfs/file.parquet")
df.write.save("file:///path/to/local/file.parquet")
如上所示,直接使用内置函数读写文本,可以使用hdfs:或者file:标注是本地文件还是HDFS文件。
与RDD的转换
DataFrame描述的是表的结构,而RDD描述的是数据集,它们之间需要转换。
DataFrame转换为RDD
这个比较简单,直接调用df.rdd即可,得到一个org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]数据集。如下是一个数据示例:
RDD转换为DataFrame
这个要稍微复杂点。假设rdd是一个类型为org.apache.spark.rdd.RDD[(String, Long)]的数据集,转换办法如下:
object schema {
val name = StructField("name", StringType, true)
val number = StructField("number", LongType, true)
val row = StructType(Array(name, number))
}
val row = rdd.map(x=>Row(x._1, x._2)) //转换为Row
df = sqlContext.createDataFrame(row, schema.row) //创建dataframe
在这里关键是创建了一个schema对象,来描述表的每个列的类型。
一些常见的SQL查询用法
select
df.select($"date".substr(0,10) as "date", $"page")
用$"column"表示一个列,注意必须用双引号,单引号会报错。substr是截取字符串,类比于SQL中的LEFT, RIGHT。as表示重命名。
返回df
where
df.where($"column1" === "" && $"column2" === 34) or
df.where($"class" === "").df.where($"id" === 32)
这个地方的等于必须使用三个等号,如果是整数值不用引号,多个查询条件可以使用&&,也可以多次调用。
返回DataFrame
count
df.count()
直接调用查询函数
返回计数
order by
df.sort(asc("column")) or df.sort(desc("column"))
使用asc或者desc函数,列作为参数
返回DataFrame
group by
df.groupBy("column1", "column2")
返回值是一个org.apache.spark.sql.GroupedData数据类型。
grouby必须结合聚合函数使用才有效。例如,
df.groupBy("column1", "column2").count()
返回值是一个包含了columns1, column2和count列的DataFrame。
举例:
df.groupBy("column1").agg(min("timestamp") as "min",max("timestamp") as "max" ,count("timestamp") as "count",max("timestamp")- min("timestamp") as "interval")
groupBy之后,对每组数据进行聚合,一些聚合操作包括max, min, count等。结合包括了column1及agg中指定的各列。
join
key.join(data,key("column1")===data("column2"),"inner")
连接查询,key和data为DataFrame。
返回值是join后的DataFrame
打印结果
df.show(10)
打印表的格式
df.take(10).foreach(println)
df.head(10).foreach(println)
打印数组格式