当前位置: 首页 > 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;通过机器学习技术&#…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...