使用pyspark

时间:2019-12-13 20:34:51

标签: python pyspark databricks

#Start and End is a range of dates. 
start = date(2019, 1, 20)
end = date(2019, 1, 22)

for single_date in daterange(start, end):
  query = "(SELECT ID, firstname,lastname,date FROM dbo.emp WHERE date = '%s' ) emp_alias" %((single_date).strftime("%Y-%m-%d %H:%M:%S")) 
  df = spark.read.jdbc(url=jdbcUrl, table=query, properties=connectionProperties)
  df.write.format("parquet").mode("ignore").partitionBy("Date").save("/mnt/data/empData.parquet")

我在表中有天数的数据,我需要按日期划分的实木复合地板文件。我必须每天保存一次,因为数据量巨大,而且我无法像年份数据那样将所有日期都放在一个数据帧中。我尝试了所有保存模式。在“忽略”模式下,它保存第一天。在“覆盖”模式下,它将保存前一天。在“附加”模式下,它将添加数据。我需要的是,如果当天有可用的数据,则应忽略当天的数据,将已有的数据保留,但是如果没有可用的数据,则在按日期划分的镶木地板文件中创建。请帮忙。

1 个答案:

答案 0 :(得分:0)

如果您还想使用Hive分区(当您调用方法{{时所要的),那么目前还没有PySpark SaveMode允许您在插入新分区时保留现有分区。 1}})。请注意,可以执行相反的操作,即覆盖某些分区中的数据,同时保留DataFrame中没有数据的分区(将配置设置Planet()设置为partitionBy,编写数据集时使用"spark.sql.sources.partitionOverwriteMode"

您仍然可以通过首先创建一组所有现有分区来实现所需的目标。您可以使用PySpark或使用允许您在文件系统(例如Azure Data Lake Storage Gen2)或键值存储(例如AWS S3)中执行列表操作的任何库来执行此操作。有了该列表后,就可以使用它来过滤新数据集以获取仍要写入的数据。这是仅包含PySpark的示例:

"dynamic"

如您所见,仅添加了2个分区。已经存在的那些已被保留。

现在,获取SaveMode.Overwrite DataFrame需要读取数据。不过,Spark实际上不会读取所有数据,而只会读取分区列和元数据。如前所述,您也可以使用与数据存储位置相关的任何API来获取此数据。在我自己的情况下,以及在您的情况下,就像您如何写入Databricks上的In [1]: from pyspark.sql.functions import lit ...: df = spark.range(3).withColumn("foo", lit("bar")) ...: dir = "/tmp/foo" ...: df.write.mode("overwrite").partitionBy("id").parquet(dir) # initial seeding ...: ! tree /tmp/foo ...: ...: /tmp/foo ├── id=0 │   └── part-00001-5d14d286-81e1-4eb1-969e-c0d8089712ce.c000.snappy.parquet ├── id=1 │   └── part-00002-5d14d286-81e1-4eb1-969e-c0d8089712ce.c000.snappy.parquet ├── id=2 │   └── part-00003-5d14d286-81e1-4eb1-969e-c0d8089712ce.c000.snappy.parquet └── _SUCCESS 3 directories, 4 files In [2]: df2 = spark.range(5).withColumn("foo", lit("baz")) ...: existing_partitions = spark.read.parquet(dir).select("id").distinct() ...: df3 = df2.join(existing_partitions, "id", how="left_anti") ...: df3.write.mode("append").partitionBy("id").parquet(dir) ...: spark.read.parquet(dir).orderBy("id").show() ...: ...: +---+---+ |foo| id| +---+---+ |bar| 0| |bar| 1| |bar| 2| |baz| 3| |baz| 4| +---+---+ 文件夹一样,我都可以简单地使用内置的Python函数existing_partitions:{{1} },并由此创建了一个DataFrame。

顺便说一下,您得到所见举止的原因是:

  1. 忽略模式

      

    在“忽略”模式下保存第一天。

    因为您使用的是for循环,并且最初可能不存在输出目录,所以将写入第一个日期分区。在for循环的所有后续迭代中,DataFrameWriter对象将不再写,因为它认为那里已经有一些数据(第一个日期为一个分区)。

  2. 覆盖模式

      

    在“覆盖”模式下,它将保存前一天。

    实际上,它在for循环的每次迭代中都保存了一个分区,但是由于您要指示DataFrameWriter覆盖,因此它将删除目录中所有以前存在的分区。因此,似乎只有最后一篇才写。

  3. 附加模式

      

    在“追加”模式下,它添加数据   这不需要进一步的解释。

一个建议:可能不需要多次读取数据库(使用for循环创建多个不同的查询和jdbc连接)。您可能可以将查询更新为/mnt,完全删除for循环并享受高效的写入。