#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")
我在表中有天数的数据,我需要按日期划分的实木复合地板文件。我必须每天保存一次,因为数据量巨大,而且我无法像年份数据那样将所有日期都放在一个数据帧中。我尝试了所有保存模式。在“忽略”模式下,它保存第一天。在“覆盖”模式下,它将保存前一天。在“附加”模式下,它将添加数据。我需要的是,如果当天有可用的数据,则应忽略当天的数据,将已有的数据保留,但是如果没有可用的数据,则在按日期划分的镶木地板文件中创建。请帮忙。
答案 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。
顺便说一下,您得到所见举止的原因是:
忽略模式
在“忽略”模式下保存第一天。
因为您使用的是for循环,并且最初可能不存在输出目录,所以将写入第一个日期分区。在for循环的所有后续迭代中,DataFrameWriter对象将不再写,因为它认为那里已经有一些数据(第一个日期为一个分区)。
覆盖模式
在“覆盖”模式下,它将保存前一天。
实际上,它在for循环的每次迭代中都保存了一个分区,但是由于您要指示DataFrameWriter覆盖,因此它将删除目录中所有以前存在的分区。因此,似乎只有最后一篇才写。
附加模式
在“追加”模式下,它添加数据 这不需要进一步的解释。
一个建议:可能不需要多次读取数据库(使用for循环创建多个不同的查询和jdbc连接)。您可能可以将查询更新为/mnt
,完全删除for循环并享受高效的写入。