Google bigquery 一个BigQueryOperator的模板化目标\u数据集\u表arg作为另一个BigQueryOperator的模板化

Google bigquery 一个BigQueryOperator的模板化目标\u数据集\u表arg作为另一个BigQueryOperator的模板化,google-bigquery,airflow,Google Bigquery,Airflow,我试图在ETL管道中将一组BigQuerySQL命令链接在一起,其中一些输出和输入将被加上时间戳 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'

我试图在ETL管道中将一组BigQuerySQL命令链接在一起,其中一些输出和输入将被加上时间戳

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
我当前的错误是(也在生产中)


如何在运算符执行之外访问模板字段?

您完全可以像现在这样从运算符外部引用宏,我正在这样做这是我的一些工作流

您是否尝试更改为:

first_output = GOOGLE_PROJECT_ID + ":" + DATASET_ID + "." + "first_output_{{ ds_nodash }}"

也许JINJA不喜欢用不同的引号连接字符串?

字段
目的地\u数据集\u表
绝对是模板化的,如图所示(1.9版,没有提供版本,所以我采用了最新版本):

我会将创建字符串更改为:

first_output = "[{project}:{dataset}.first_output_{{{{ ds_nodash }}}}]".format(
    project=GOOGLE_PROJECT_ID,
    dataset=DATASET_ID)
四个大括号应该变成两个,得到的字符串应该是

[my_project:my_dataset.first_output_{{ ds_nodash }}]
现在,在
目标数据集\u表中使用时,应解析
ds\u nodash

注意,我还为遗留语句添加了所需的括号
[]
。我不确定这是否也与缺少的括号有关

编辑

正如@mask正确指出的,您正在使用
second\u op
params
中的
first\u op
中的字符串,我在开头没有看到这个字符串

这不起作用,原因如下:

  • first_op不应该提供字符串,但应该使用
    first_output
    ——我仍然想知道为什么这会在第一时间起作用
  • 如果从任务中提取字符串,如果不确保字段已被处理(如Mask所述),则不会获得渲染字符串,而是始终获得原始模板字符串*
  • params
    只是没有模板化,因此无法正确更新
以下是我能想到的解决方案:

  • 派生您自己的
    BigDataOperator
    ,并将
    params
    添加到模板化字段中(如果这样做有效,则为dict)
  • 或者扩展
    xxx\u two.sql
    ,使其既不使用
    参数输入表
    ,也不使用
    第一次输出
    。由于希望
    first\u output
    在模板中可用,因此必须首先将其添加到DAG参数
    用户定义的\u宏中
    

要了解有关这些解决方案的更多信息,请查看以下相关问题:

您正在将未呈现的模板表名作为参数发送到第二个_op

在对任务实例调用第一个操作之前,
first\u op.destination\u dataset\u table
的值被分配给
input\u table
。当bql在
second_op
中呈现时,它只转换参数值,因此返回:

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所述的第一个_输出

这可能不是一个可行的解决方案,除非您的SQL与示例一样小,或者您希望SQL位于DAG文件中的某个位置

编辑:

由于在DAG的定义中添加了temaplate_searchpath,因此可以按如下方式更新
XXX_two.sql

SELECT * FROM [{{ params.input_table }}_{{ ds_nodash }}]
这允许您传递上一个操作中的表名,但将呈现BQ表分区的任务留给操作员。如果从同一个DAG调用每个操作符/任务_实例,这将解决您的问题

您可以更新到:

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},...

很抱歉忘记了版本。我是1.9.0。跟进问题。在控制台中工作。我是否应该能够创建第一个_op对象,然后立即访问目标_dataset_表并查看填充的模板?基本上我想知道模板什么时候被渲染?在对象创建的初始阶段还是在执行过程中?这两个答案在什么方面有用?在进行建议的更改后,您是否仍收到相同的错误消息?错误中显示“无效的表名:xxx:xx.first\u output{{ds\u nodash}”,实际的表名是什么?@tobi6:我不确定-
…从任务中提取字符串时,您不会得到呈现的字符串,但总是原始字符串,模板字符串
必须为true。如果确保在调用task_instance.render_templates后访问渲染字符串,则可以提取该字符串。很好,你有没有可能发布一个访问类成员变量并返回模板的简短完整的工作示例?很好的建议。不幸的是,我希望我的SQL在python字符串之外(它非常大)。
SELECT * FROM [{{ params.input_table }}_{{ ds_nodash }}]
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},...