__HIVE_DEFAULT_PARTITION__作为粘合ETL作业中的分区值

时间:2019-03-19 17:31:19

标签: apache-spark amazon-s3 pyspark boto3 aws-glue

我有CSV数据,这些数据通过粘合爬网程序进行了爬网,并最终出现在一个表中。

我正在尝试运行ETL作业,以将磁盘上的数据重新分区为date列的某些组件。然后将CSV转换为实木复合地板。

即我的数据中有一列名为“日期”的列,并希望将数据划分为s3上的年,月,日分区。

我能够转换为镶木地板并将其正确划分为序列号值(在不同的列上),但是它将与日期有关的所有年份,月份和日期的值都放入了“ __HIVE_DEFAULT_PARTITION__”分区。

我能够在其他列上进行分区(例如序列号),但是年/月/日不在原始数据集中,因此我的方法是从日期列创建值作为新列在数据集中,并告诉write_dynamic_frame函数按列进行分区,但这不起作用。

一般来说,我是Spark / pyspark和胶水的新手,所以很可能我缺少一些简单的东西。

感谢提供帮助的任何人。

import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.sql import functions as F
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.dynamicframe import DynamicFrame
from awsglue.job import Job


args = getResolvedOptions(sys.argv, ['JOB_NAME'])

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)

datasource0 = glueContext.create_dynamic_frame.from_catalog(database = "my_database", table_name = "my_table", transformation_ctx = "datasource0")
applymapping1 = ApplyMapping.apply(frame = datasource0, mappings = [("date", "date", "date", "date"), ("serial-number", "string", "serial-number", "string")], transformation_ctx = "applymapping1")
resolvechoice2 = ResolveChoice.apply(frame = applymapping1, choice = "make_struct", transformation_ctx = "resolvechoice2")
dropnullfields3 = DropNullFields.apply(frame = resolvechoice2, transformation_ctx = "dropnullfields3")

to_spark_df4 = dropnullfields3.toDF()

with_file_name_df5 = to_spark_df4.withColumn("input_file_name", F.input_file_name()).withColumn('year', F.year(F.col("date").cast("date"))).withColumn('month', F.month(F.col("date").cast("date"))).withColumn('day', F.dayofmonth(F.col("date").cast("date")))

back_to_glue_df8 = DynamicFrame.fromDF(with_file_name_df5, glueContext, "back_to_glue_df8")


datasink4 = glueContext.write_dynamic_frame.from_options(frame = back_to_glue_df8, connection_type = "s3", connection_options = {"path": "s3://output/path","partitionKeys": ["serial-number","year", "month","day"]}, format = "parquet", transformation_ctx = "datasink4")
job.commit()

结果是我在s3中的键最终看起来像这样:

serial-number=1234567890/year=__HIVE_DEFAULT_PARTITION__/month=__HIVE_DEFAULT_PARTITION__/day=__HIVE_DEFAULT_PARTITION__/part-01571-273027e4-72ba-45ff-ac15-c0bb2f342e58.c000.snappy.parquet

更新:已针对格式进行修改

1 个答案:

答案 0 :(得分:2)

我从事的工作与您非常相似。我希望您现在能够解决它,但是无论如何,这是解决您困境的方法:

基本解决方案:

from pyspark.sql.functions import year, month, dayofmonth

###### rest of your code until ApplyMapping included ######

# add year, month & day columns, non zero-padded
df = df.toDF()
df = df.withColumn('year', year(df.date))\
       .withColumn('month', month(df.date))\
       .withColumn('day', dayofmonth(df.date))

附加说明:

如果需要在要选择日期范围的Athena上运行查询,我建议您避免使用嵌套分区(例如year-> month-> day),而应使用平面分区模式。这样做的原因是查询变得更容易编写。这是获取平面模式的python代码:

from pyspark.sql.functions import date_format

###### rest of your code until ApplyMapping included ######

df = df.toDF()
df = df.withColumn('date_2', date_format(df.date, 'yyyy-MM-dd'))

# date_2 is because column "date" already exists,
# but we want the partitioning one to be in a string format.
# You can later drop the original column if you wish.

假设现在要查询从2020年3月15日到2020年4月3日的数据。

这是基于您选择的分区模式的SQL查询。

嵌套模式

SELECT item_1, item_2
  FROM my_table
 WHERE year = 2020
   AND (
          (month = 3 AND day >= 15)
       OR (month = 4 AND day <= 3)
       )

平面模式

SELECT item_1, item_2
  FROM my_table
 WHERE date BETWEEN '2020-03-15' AND '2020-04-3'

此外,假设您的“日期”列存储为字符串,则可以使用LIKE运算符运行查询。

例如,如果要查询数据库中每个四月的所有数据,可以执行以下操作:

SELECT item_1, item_2
  FROM my_table
 WHERE date LIKE '%-04-%'