我正在尝试在ETL管道中将一堆BigQuery SQL命令链接在一起,其中一些输出和输入将带有时间戳。
from datetime import timedelta
import airflow
from airflow import DAG
from airflow.contrib.operators.bigquery_operator import BigQueryOperator
DAG_NAME = 'foo'
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': airflow.utils.dates.days_ago(7),
'email': ['xxx@xxx.com'],
'email_on_failure': True,
'email_on_retry': False,
'retries': 1,
'retry_delay': timedelta(minutes=1),
}
dag = DAG(
dag_id="blah",
default_args=default_args,
schedule_interval=None,
template_searchpath=["/usr/local/airflow/dags/xxx/sql"])
GOOGLE_PROJECT_ID = 'xxx'
DATASET_ID = 'xxx'
first_output = GOOGLE_PROJECT_ID + ":" + DATASET_ID + "." + "first_output_" + '{{ ds_nodash }}'
second_output = GOOGLE_PROJECT_ID + ":" + DATASET_ID + "." + "second_output"
GOOGLE_CLOUD_PLATFORM_CONNECTION_ID="google_cloud_default"
first_op = BigQueryOperator(
task_id='first_output',
dag=dag,
bigquery_conn_id=GOOGLE_CLOUD_PLATFORM_CONNECTION_ID,
bql="XXX.sql",
use_legacy_sql=True,
allow_large_results=True,
destination_dataset_table=first_output # {{ ds }} gets substituted because destination_dataset_table is a templated field
)
second_op = BigQueryOperator(
task_id='second_op',
dag=dag,
bigquery_conn_id=GOOGLE_CLOUD_PLATFORM_CONNECTION_ID,
bql="XXX_two.sql", # XXX_two.sql contains a {{ params.input_table }} reference
params={'input_table': first_op.destination_dataset_table},
use_legacy_sql=True,
allow_large_results=True,
destination_dataset_table=second_output
)
second_op.set_upstream(first_op)
XXX_two.sql的内容:
SELECT * FROM [{{ params.input_table }}
通过以下方式进行测试:
airflow test blah second_op 2015-06-01
我当前的错误是(也在生产中)
Exception: BigQuery job failed. Final error was: {'reason': 'invalid', 'location': BLAH, 'message': 'Invalid table name: xxx:xx.first_output_{{ ds_nodash }}'}.
如何在执行操作员之外访问模板化字段?
答案 0 :(得分:4)
字段destination_dataset_table
绝对是模板的,可以看到in the source code(在1.9中,没有提供版本,所以我使用了最新的版本):
template_fields = ('bql', 'destination_dataset_table')
我将创建字符串更改为:
first_output = "[{project}:{dataset}.first_output_{{{{ ds_nodash }}}}]".format(
project=GOOGLE_PROJECT_ID,
dataset=DATASET_ID)
四个大括号应该变成两个,并且结果字符串应该看起来像
[my_project:my_dataset.first_output_{{ ds_nodash }}]
现在ds_nodash
在destination_dataset_table
中使用时应该被解析。
请注意,我还为旧声明添加了所需的括号[ ]
。我不确定这是否也可能与缺少的括号有关。
编辑
正如@mask正确说明的那样,您正在使用first_op
second_op
中params
中的字符串,我一开始没有看到它。
由于以下原因,此方法不起作用:
first_output
-我仍然想知道为什么这首先起作用params
只是未模板化,因此无法正确更新这些是我能想到的解决方案:
BigDataOperator
并将params
添加到模板字段(如果在b / c中有效,则是字典)xxx_two.sql
,使其不使用params.input_table
,但也使用first_output
。由于您希望first_output
在模板中可用,因此必须首先将其添加到DAG参数user_defined_macros
中。 要查看有关这些解决方案的更多信息,请查看以下相关问题:Make custom Airflow macros expand other macros
答案 1 :(得分:2)
您绝对可以像您正在做的那样从运算符外部引用宏,我正在做这是我的一些工作流程。
您是否尝试过更改为:
first_output = GOOGLE_PROJECT_ID + ":" + DATASET_ID + "." + "first_output_{{ ds_nodash }}"
也许JINJA不喜欢用不同引号引起的字符串连接?
答案 2 :(得分:2)
您正在将未呈现的模板化表名作为参数发送给second_op。
在任务实例first_op.destination_dataset_table
上调用render_templates之前,将input_table
的值分配给first_op
。当在second_op
中呈现bql时,它仅转换参数的值并因此返回:
SELECT * FROM xxx:xx.first_output_{{ ds_nodash }}
如果将bql转换为字符串,则可以使用,例如:
BigQueryOperator(task_id='second_op',...,
bql='SELECT * FROM [{table}]'.format(table=first_op.destination_dataset_table)
并设置@ tobi6提到的first_output。
除非您的SQL与示例一样小,或者您愿意将SQL放在DAG文件中的某个位置,否则这可能不是可行的解决方案。
编辑:
由于在DAG的定义中添加了temaplate_searchpath,因此可以按以下方式更新XXX_two.sql
:
SELECT * FROM [{{ params.input_table }}_{{ ds_nodash }}]
这允许您传递上一个操作中的表名,但将渲染BQ表分区的任务留给了Airflow操作员。如果从同一DAG调用每个operator / task_instance,则可以解决您的问题。
您可以更新到:
first_ouput = GOOGLE_PROJECT_ID + ":" + DATASET_ID + "." + "first_output"
first_op = BigQueryOperator(...,destination_dataset_table= "{}_{{{{ ds_nodash }}}}".format(first_ouput))
second_op = BigQueryOperator(..., params={'input_table': first_output},...