当前位置: 首页 > news >正文

如何使用Jinja定义dbt宏

dbt宏在dbt框架内的工作方式与传统编程中的函数类似。它允许用户将特定的、通常是重复的SQL逻辑封装到可调用的命名单元中,就像在其他编程语言中用函数来避免重复代码一样;dbt宏定义特定业务的SQL逻辑,然后在dbt项目中需要的地方调用该宏函数实现代码重用。数据工程师利用宏在数据开发项目中维护更干净、更模块化的代码,因此,我们可以把dbt宏看作:为SQL提供强大的、类似函数的强大功能!

dbt 宏简介

dbt宏使用Jinja引擎,这是Python web框架中广泛采用的模板引擎。使用Jinja可以将占位符和逻辑直接嵌入到SQL代码中,使其具有动态性和模块化。在dbt宏中可以定义带有参数的SQL块,然后可以在整个dbt项目中使用不同的参数重用该SQL块。

为什么要使用Jinja宏?

  • 可重用性: 一次定义复杂逻辑,然后跨多个模型使用它。
  • 可维护性: 集中逻辑以简化更新并减少错误。
  • 灵活性: 在宏中组合SQL和Python代码来处理复杂的数据转换。

简单宏示例

下面示例实现计算百分比逻辑,当分母为0直接返回0,反之,对两数相除的结果乘以100转为百分比值:

{% macro calculate_percentage(numerator, denominator) %}CASE WHEN {{ denominator }} = 0 THEN 0ELSE ({{ numerator }} / {{ denominator }} * 100)END
{% endmacro %}

在你的DBT模型中(例如,models/my_model.sql),你现在可以调用这个宏:

SELECTorder_id,{{ calculate_percentage('total_revenue', 'total_cost') }} AS profit_margin
FROMorders

这个SQL查询使用calculate_percentage宏计算每个订单的利润率。

Jinja 引擎介绍

通过将dbt宏与Jinja模板引擎一起使用,SQL代码变得更加可重用、可维护和模块化。它让数据开发工作避免冗余、提升一致性、并简化开发过程。Jinja为宏提供了构建块,因此深入理解Jinja语法是编写高质量宏的关键。接下来,让我们通过创建dbt宏示例学习Jinja语法。

变量和占位符

  • 变量

    语法: {% set var_name ... %}

在dbt宏中使用Jinja时,可以使用set将值赋给变量,从而可以在宏中引用或重用变量。在下面的示例中,定义了一个名为partners的变量,其中包含代理产品的三个伙伴的列表。在本文后面回顾for循环时,我们将利用这个变量。

示例:

{% set partners =  ['costco', 'sams_club', 'target'] %}
  • 占位符

​ 语法:{{...}}

双花括号之间的任何内容都将被计算并替换为实际值。在dbt中,这种语法最常用于函数ref()和source()。在下面的示例中,引用了users关系模型。

select organization_id, min(created_at) as first_user_created_atfrom {{ ref('users') }}
group by organization_id;

ref(‘users’)是一个dbt函数,它被替换为dbt项目中users模型对应的关系:可能为视图、表或物化视图等。当上面的代码被编译时,生成的代码大致如下:

select organization_id, min(created_at) as first_user_created_atfrom dbt.users
group by organization_id;

注释

语法:{#...#}

{#和#}之间的任何内容都是注释,不会包含在最终的SQL中。通常只有在需要记录有关Jinja逻辑的特定内容时才需要这样做。应该优先描述dbt项目中宏逻辑、输入参数描述,而不是代码注释。

示例:

{# 这里内容仅在宏源文件中出现,编译后的内容不包括注释信息 #}

if 语句

语法:{% if... %} … {% endif %}

这种语法用于简单的条件(if语句)来添加/删除SQL行。例如,在下面的dbt宏示例中,只有在满足条件时才会将where语句添加到查询中。在这种情况下,条件是模型正在以增量方式运行。如果模型没有以增量方式运行,where语句将被排除在查询之外。

{% if is_incremental() %}where last_update_at > (select max(last_update_at) from {{ this }}){% endif %}

使用Jinja可以构建非常复杂的逻辑,但建议尽量保持简单易读。大多数使用dbt的人都是数据分析师,他们需要了解SQL查询是如何工作的,并且可能没有时间学习Jinja中所有复杂的逻辑运算符。

loop 循环

语法: {% for... %}

我们还可以使用Jinja for循环遍历列表或序列。这对dbt宏中非常有用,可以实现在SQL中很难处理的逻辑。在下面的dbt宏示例中,我们使用partners变量创建合作伙伴列表。对于每个合作伙伴,单独存储在各自的schema中,但是销售表的列完全相同。我们需要把这些表合并在一起,以得到一个完整的合作伙伴销售名单。

{% set partners =  ['costco', 'sams_club', 'target'] %}{% for partner in partners %}select *,'{{ partner }}' as partnerfrom {{ source(partner, 'sales') }}{# this adds union all to all selects except for the last one #}{% if not loop.last %} union all {% endif %}{% endfor %}

编译结果大致如下文:

select *, 'costco' as partner
from costco.sales union all select *, 'sams_club' as partner
from sams_club.sales union all select *, 'target' as partner
from target.sales 

我们看到在模型文件中直接使用for循环,使得代码量减少很多。如果还有其他表(如:客户、库存等)需要合并,只要稍微调整下上述代码即可实现重用。

宏示例实战

下面再通过两个示例加强对宏的认识,提升编写宏函数能力。

生成动态条件

Jinja是生成动态SQL的强大工具。下面是一个基于jinja的宏例子,它根据提供的条件生成一个WHERE子句:

{% macro generate_where_clause(column_name, value) %}{% if value is not none %}WHERE {{ column_name }} = '{{ value }}'{% else %}-- No condition{% endif %}
{% endmacro %}

在模型中使用宏:

SELECT*
FROM{{ ref('customer_data') }}
{{ generate_where_clause('customer_id', customer_id) }};

这种方法简化了SQL生成,并在查询中提供了更大的灵活性。

动态日期范围

假设您需要根据动态日期范围筛选记录,例如“最近7天”或“当前月份”。你可以创建一个宏来处理这个问题:

{% macro dynamic_date_filter(date_column, period) %}{% if period == 'last_7_days' %}{{ date_column }} BETWEEN CURRENT_DATE - INTERVAL '7 days' AND CURRENT_DATE{% elif period == 'current_month' %}{{ date_column }} BETWEEN DATE_TRUNC('month', CURRENT_DATE) AND CURRENT_DATE{% else %}1=1 -- No filter{% endif %}
{% endmacro %}

该宏接受日期列和周期作为参数,返回适当的SQL筛选器。下面代码展示如何在模型中使用宏:

SELECTuser_id,order_date,total_amount
FROMsales
WHERE{{ dynamic_date_filter('order_date', 'last_7_days') }}

该查询过滤销售表,只返回最近7天的记录。

宏文档

除了宏代码中的注释外,还应该在宏描述文件中添加宏及和输入参数的描述。就像在模型描述文件中对列进行描述文档一样,宏文档使用相同的语法。您可以在macros文件夹中创建macros.yml文件,以便有专门的地方描述宏文档。下面是一个示例宏描述文件,它定义了两个宏的文档:

version: 2macros:- name: cents_to_dollarsdescription: A macro to convert cents to dollarsarguments:- name: column_nametype: stringdescription: The name of the column you want to convert- name: precisiontype: integerdescription: Number of decimal places. Defaults to 2.- name: from_jsondescription: A macro to extract a nested column value out of a JSON column.arguments:- name: json_column_nametype: variantdescription: The name of the JSON column that contains the nested column.- name: nested_column_nametype: stringdescription: The name of the nested column to be extracted from the JSON column. 

注意事项:在编写宏之后需要在模型中使用它以确保它按预期工作。由于宏本质上是模板,因此需要从模型或另一个宏调用它们以查看最终的SQL输出。

最佳实践;

dbt宏的主要目标是使SQL代码更易于维护并减少冗余。随着对dbt和Jinja的使用经验不断积累,创建宏的过程将更顺畅自然,从而让你能够专注于复杂数据转换和模型业务实现。虽然Jinja宏功能强大,但以下最佳实践仍值得借鉴:

  • 保持简单: 避免创建难以调试或过于复杂的宏。将复杂的逻辑分解为更小的、可重用的宏。
  • 文档化宏: 给宏添加注释说明它们的用途和使用场景,通过文档描述参数、数据类型及意义。
  • 测试宏: 在部署到生产环境之前,要对宏进行彻底测试,以确保它的行为符合预期。
  • 版本控制: 在版本控制中跟踪对宏的更改;
  • 重复利用Jinja内置的宏函数、知名第三方已有宏函数,后续文章会详细介绍。

总结

掌握 DBT Core 里的 Jinja 宏能够极大地提升数据工程师的工作效率。通过创建可重复使用的代码片段,能够对复杂的逻辑进行标准化处理,减少代码重复现象,同时维护干净、高效的数据模型。我们可以从尝试简单的宏着手,随着对语法越来越熟悉,逐步编写更为复杂的宏。

期待您的真诚反馈,更多内容请阅读数据分析工程专栏。

相关文章:

如何使用Jinja定义dbt宏

dbt宏在dbt框架内的工作方式与传统编程中的函数类似。它允许用户将特定的、通常是重复的SQL逻辑封装到可调用的命名单元中,就像在其他编程语言中用函数来避免重复代码一样;dbt宏定义特定业务的SQL逻辑,然后在dbt项目中需要的地方调用该宏函数…...

深入理解 JavaScript 三大作用域:全局作用域、函数作用域、块级作用域

一. 作用域 对于多数编程语言,最基本的功能就是能够存储变量当中的值、并且允许我们对这个变量的值进行访问和修改。那么有了变量之后,应该把它放在哪里、程序如何找到它们?是否需要提前约定好一套存储变量、访问变量的规则?答案…...

【门牌制作 / A】

题目 代码 #include <bits/stdc.h> using namespace std; int main() {int cnt 0;for (int i 1; i < 2020; i){string s;s to_string(i);cnt count(s.begin(), s.end(), 2);}cout << cnt; }...

Git+Jenkins 基本使用(Basic Usage of Git+Jenkins)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...

智谱清言:智能语音交互的引领者,解锁高效沟通新体验

哪个编程工具让你的工作效率翻倍&#xff1f; 在日益繁忙的工作环境中&#xff0c;选择合适的编程工具已成为提升开发者工作效率的关键。不同的工具能够帮助我们简化代码编写、自动化任务、提升调试速度&#xff0c;甚至让团队协作更加顺畅。那么&#xff0c;哪款编程工具让你…...

前端组件库

vant2现在的地址 Vant 2 - Mobile UI Components built on Vue...

后端常用的mybatis-plus方法以及配合querywapper使用

目录 一、插入数据 save方法 二、删除操作 removeById方法 三、更新操作 updateById方法 四、查询操作 selectById方法 五、条件构造器QueryWrapper的更多用法 1.比较操作符 2.逻辑操作符 3.模糊查询 4.空值判断 一、插入数据 save方法 save(T entity):向数据库中插入…...

【设计模式】万字详解:深入掌握五大基础行为模式

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 【设计模式】&#xf…...

C++ 9.19

练习&#xff1a;要求在堆区申请5个double类型的空间&#xff0c;用于存储5名学生的成绩。请自行封装函数完成 1> 空间的申请 2> 学生成绩的录入 3> 学生成绩的输出 4> 学生成绩进行降序排序 5> 释放申请的空间 主程序中用于测试上述函数 #include<ios…...

[Unity Demo]从零开始制作空洞骑士Hollow Knight第五集:再制作更多的敌人

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作敌人另个爬虫Crawler 1.公式化导入制作另个爬虫Crawler素材2.制作另个爬虫Crawler的Crawler.cs状态机3.制作敌人另个爬虫Crawler的playmaker状态机二、…...

怎么把excel翻译成英文?这些翻译技巧记得收藏

在处理Excel数据时&#xff0c;我们常常会遇到多语言的数据集&#xff0c;这无疑给数据分析和整理带来了不小的挑战。 幸运的是&#xff0c;随着技术的发展&#xff0c;现在有多种工具可以帮助我们进行Excel中的批量翻译&#xff0c;这些工具以其强大的翻译功能和便捷的操作方…...

信息技术引领的智能化未来

信息技术引领的智能化未来 随着信息技术的飞速发展&#xff0c;社会各个领域正在加速迈入智能化的新时代。信息技术的广泛应用&#xff0c;尤其是人工智能、大数据、物联网等前沿技术的创新与融合&#xff0c;正在从根本上改变着人们的生产和生活方式。本文将探讨信息技术在智…...

【QT开发-Pyside】使用Pycharm与conda配置Pyside环境并新建工程

知识拓展 Pycharm 是一个由 JetBrains 开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它主要用于 Python 编程语言的开发。Pycharm 提供了代码编辑、调试、版本控制、测试等多种功能&#xff0c;以提高 Python 开发者的效率。 Pycharm 与 Python 的关系 Pycharm 是…...

vue选项式写法项目案例(购物车)

一、初始化项目结构 1.初始化vite项目 npm create vite cd vite-project npm install 2.清理项目结构 清空App.vue 删除components目录下的HelloWorld.vue组件 3.为组件的样式启用sacc或less组件 npm i sass4.初始化index.css全局样式 :root{font-size:12px } 二、封装…...

[Linux][进程] 认识进程

基本概念 进程是一个操作系统术语,用来管理与操作程序.在windows下打开任务管理器即可查看目前打开的所有进程 PCB 进程控制块,从代码层面来说 PCB 是进程所有属性的一个结构体,在Linux源码中PCB指的是struct task_struct. Linux环境下: 进程 task_struct 代码 …...

如何安装和注册 GitLab Runner

如何安装和注册 GitLab Runner GitLab Runner 是一个用于运行 GitLab CI/CD (Continuous Integration/Continuous Deployment) 作业。它是一个与 GitLab 配合使用的应用程序&#xff0c;可以在本地或云中运行。Runner 可以执行不同类型的作业&#xff0c;例如编译代码、运行测…...

专业学习|动态规划(概念、模型特征、解题步骤及例题)

一、引言 &#xff08;一&#xff09;从斐波那契数列引入自底向上算法 &#xff08;1&#xff09;知识讲解 &#xff08;2&#xff09;matlap实现递归 &#xff08;3&#xff09;带有备忘录的遗传算法 &#xff08;4&#xff09;matlap实现带有备忘录的递归算法 “&#xff1…...

数据结构与算法 #时间复杂度 #空间复杂度

文章目录 前言 一、算法的复杂度 二、时间复杂度 三、空间复杂度 四、例题 1、例1&#xff1a;冒泡排序 2、例2&#xff1a; 3、例3&#xff1a; 4、例4: 二分查找 5、例5: 阶乘 6、例6: 斐波那契 五、常见算法复杂度 总结 前言 路漫漫其修远兮&#xff0c;吾将上下而求索&…...

【多机器人轨迹规划最优解问题】

此类应用场景通常很难有严格意义上的最优解&#xff0c;一般只能得到较优解。限制其获得最优解的主要因素如下&#xff1a; 一、问题的复杂性 多机器人系统的高维度性&#xff1a;每台机器人都有自己的位置、速度、任务等多个状态变量&#xff0c;多台机器人组合在一起使得问…...

机器学习及其应用领域【金融领域】

机器学习及其应用领域【金融领域】 一、智能投顾与资产配置二、信贷审批与风险评估三、支付与交易安全四、金融欺诈检测五、市场预测与情绪分析六、客户服务与个性化推荐七、面临的挑战与未来趋势八、总结 一、智能投顾与资产配置 智能投顾&#xff1a;通过机器学习技术&#…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...