Python 如何从按月分区的拼花地板文件中删除特定月份

Python 如何从按月分区的拼花地板文件中删除特定月份,python,apache-spark,pyspark,parquet,Python,Apache Spark,Pyspark,Parquet,我拥有过去5年的月度收入数据,并以append模式,但month列的格式存储各个月份的数据帧。下面是伪代码- def Revenue(filename): df = spark.read.load(filename) . . df.write.format('parquet').mode('append').partitionBy('month').save('/path/Revenue') Revenue('Revenue_201501.csv') Reven

我拥有过去5年的
月度收入数据,并以
append
模式,但
month
列的格式存储各个月份的数据帧。下面是伪代码-

def Revenue(filename):
    df = spark.read.load(filename)
    .
    .
    df.write.format('parquet').mode('append').partitionBy('month').save('/path/Revenue')

Revenue('Revenue_201501.csv')
Revenue('Revenue_201502.csv')
Revenue('Revenue_201503.csv')
Revenue('Revenue_201504.csv')
Revenue('Revenue_201505.csv')
df
每月以
parquet
格式存储,如下所示-

问题:如何删除与特定月份对应的
parquet
文件夹

一种方法是在一个大的
df
中加载所有这些
parquet
文件,然后使用
.where()
子句筛选出特定月份,然后在
overwrite
模式下将其保存回
parquet
格式
partitionBy
月份,如下所示-

# If we want to remove data from Feb, 2015
df = spark.read.format('parquet').load('Revenue.parquet')
df = df.where(col('month') != lit('2015-02-01'))
df.write.format('parquet').mode('overwrite').partitionBy('month').save('/path/Revenue')
但是,这种方法相当麻烦

另一种方法是直接删除特定月份的文件夹,但我不确定这是否是正确的方法,以免我们以不可预见的方式更改
元数据


删除特定月份的
parquet
数据的正确方法是什么?

Spark支持删除分区,包括数据和元数据。
引用scala代码注释

/**
 * Drop Partition in ALTER TABLE: to drop a particular partition for a table.
 *
 * This removes the data and metadata for this partition.
 * The data is actually moved to the .Trash/Current directory if Trash is configured,
 * unless 'purge' is true, but the metadata is completely lost.
 * An error message will be issued if the partition does not exist, unless 'ifExists' is true.
 * Note: purge is always false when the target is a view.
 *
 * The syntax of this command is:
 * {{{
 *   ALTER TABLE table DROP [IF EXISTS] PARTITION spec1[, PARTITION spec2, ...] [PURGE];
 * }}}
 */
在您的情况下,没有支持表。 我们可以将dataframe注册为临时表,并使用上面的语法()

在pyspark中,我们可以使用以下语法运行SQL 样本:


下面的语句将只删除与分区信息相关的元数据

ALTER TABLE db.yourtable DROP IF EXISTS PARTITION(loaded_date="2019-08-22");
如果还要删除数据,则需要将配置单元外部表的TBLProperty设置为False。它会将您的配置单元表设置为托管表

alter table db.yourtable set TBLPROPERTIES('EXTERNAL'='FALSE');
您可以将其设置回外部表

alter table db.yourtable set TBLPROPERTIES('EXTERNAL'='TRUE');
我尝试使用spark会话设置给定属性,但遇到了一些问题

 spark.sql("""alter table db.test_external set tblproperties ("EXTERNAL"="TRUE")""")
pyspark.sql.utils.AnalysisException: u"Cannot set or change the preserved property key: 'EXTERNAL';"
我相信一定有办法做到这一点。我最终使用了python。我在pyspark中定义了下面的函数,它完成了这项工作

query=""" hive -e 'alter table db.yourtable set tblproperties ("EXTERNAL"="FALSE");ALTER TABLE db.yourtable DROP IF EXISTS PARTITION(loaded_date="2019-08-22");' """

def delete_partition():
        print("I am here")
        import subprocess
        import sys
        p=subprocess.Popen(query,shell=True,stderr=subprocess.PIPE)
        stdout,stderr = p.communicate()
        if p.returncode != 0:
            print stderr
            sys.exit(1) 

>>> delete_partition()
这将同时删除元数据和数据。 笔记我已经用Hive ORC外部分区表对此进行了测试,该表在加载日期进行分区

# Partition Information
# col_name              data_type               comment

loaded_date             string
更新: 基本上,您的数据位于名为

/Revenue/month=2015-02-01
/Revenue/month=2015-03-01
/Revenue/month=2015-03-01
等等

def delete_partition(month_delete):
      print("I am here")
      hdfs_path="/some_hdfs_location/Revenue/month="
      final_path=hdfs_path+month_delete
      import subprocess
      subprocess.call(["hadoop", "fs", "-rm", "-r", final_path])
      print("got deleted")

delete_partition("2015-02-01")

如果您稍后选择,这里有一个链接可以进行很好的讨论,但它不是您原始问题的答案。张贴它只是为了参考@vikrantrana非常感谢Vikrant推荐我访问链接。让我试着理解一下。请看下面的答案。它可以作为指向原始问题的指针。您必须根据拼花地板格式或您的分区列做一些更改。如果你想用spark函数实现它,也请告诉我。这个问题似乎已经讨论了很久了。不太确定,但可能是。你需要做第一部分吗?df=。。。我倾向于在脚本中这样做。如果是这样的话,那就很有趣了。我们可以像中提到的那样直接编写SQL。我不确定
altertable
如何符合链接中提到的语法。@DaRkMaN嗨!根据您的回答,我发现使用
PURGE
选项的
Alter TABLE
仅在
HDFS
上的数据为
内部表时删除,而不是
外部表时删除。我的是
external
one。如何从HDFS中删除相应的数据?正如您在评论中提到的,此
ALTER TABLE…
code不适合PySpark框架。也许它可以在HIVE上运行,但我必须在
PySpark
中运行。谢谢你的努力。好吧,我会在周一试试,然后通知你。好吧,我试着调查一下,但是因为我在蜂巢中没有代码,而是直接在
PySpark
Jupyter
,所以我不知道在我的情况下会是什么。。您正在将数据帧直接保存到某个hdfs位置。我来检查一下。谢谢,是的,你完全正确。我已将我的
df
parquet
格式保存在HDFS上,按
month
分区,如问题所示。我正在从那里直接加载我的
df
。Vikrant:)
def delete_partition(month_delete):
      print("I am here")
      hdfs_path="/some_hdfs_location/Revenue/month="
      final_path=hdfs_path+month_delete
      import subprocess
      subprocess.call(["hadoop", "fs", "-rm", "-r", final_path])
      print("got deleted")

delete_partition("2015-02-01")