如何使用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)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...
智谱清言:智能语音交互的引领者,解锁高效沟通新体验
哪个编程工具让你的工作效率翻倍? 在日益繁忙的工作环境中,选择合适的编程工具已成为提升开发者工作效率的关键。不同的工具能够帮助我们简化代码编写、自动化任务、提升调试速度,甚至让团队协作更加顺畅。那么,哪款编程工具让你…...

前端组件库
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):向数据库中插入…...

【设计模式】万字详解:深入掌握五大基础行为模式
作者:后端小肥肠 🍇 我写过的文章中的相关代码放到了gitee,地址:xfc-fdw-cloud: 公共解决方案 🍊 有疑问可私信或评论区联系我。 🥑 创作不易未经允许严禁转载。 姊妹篇: 【设计模式】…...

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

[Unity Demo]从零开始制作空洞骑士Hollow Knight第五集:再制作更多的敌人
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、制作敌人另个爬虫Crawler 1.公式化导入制作另个爬虫Crawler素材2.制作另个爬虫Crawler的Crawler.cs状态机3.制作敌人另个爬虫Crawler的playmaker状态机二、…...

怎么把excel翻译成英文?这些翻译技巧记得收藏
在处理Excel数据时,我们常常会遇到多语言的数据集,这无疑给数据分析和整理带来了不小的挑战。 幸运的是,随着技术的发展,现在有多种工具可以帮助我们进行Excel中的批量翻译,这些工具以其强大的翻译功能和便捷的操作方…...
信息技术引领的智能化未来
信息技术引领的智能化未来 随着信息技术的飞速发展,社会各个领域正在加速迈入智能化的新时代。信息技术的广泛应用,尤其是人工智能、大数据、物联网等前沿技术的创新与融合,正在从根本上改变着人们的生产和生活方式。本文将探讨信息技术在智…...

【QT开发-Pyside】使用Pycharm与conda配置Pyside环境并新建工程
知识拓展 Pycharm 是一个由 JetBrains 开发的集成开发环境(IDE),它主要用于 Python 编程语言的开发。Pycharm 提供了代码编辑、调试、版本控制、测试等多种功能,以提高 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 配合使用的应用程序,可以在本地或云中运行。Runner 可以执行不同类型的作业,例如编译代码、运行测…...

专业学习|动态规划(概念、模型特征、解题步骤及例题)
一、引言 (一)从斐波那契数列引入自底向上算法 (1)知识讲解 (2)matlap实现递归 (3)带有备忘录的遗传算法 (4)matlap实现带有备忘录的递归算法 “࿱…...

数据结构与算法 #时间复杂度 #空间复杂度
文章目录 前言 一、算法的复杂度 二、时间复杂度 三、空间复杂度 四、例题 1、例1:冒泡排序 2、例2: 3、例3: 4、例4: 二分查找 5、例5: 阶乘 6、例6: 斐波那契 五、常见算法复杂度 总结 前言 路漫漫其修远兮,吾将上下而求索&…...
【多机器人轨迹规划最优解问题】
此类应用场景通常很难有严格意义上的最优解,一般只能得到较优解。限制其获得最优解的主要因素如下: 一、问题的复杂性 多机器人系统的高维度性:每台机器人都有自己的位置、速度、任务等多个状态变量,多台机器人组合在一起使得问…...
机器学习及其应用领域【金融领域】
机器学习及其应用领域【金融领域】 一、智能投顾与资产配置二、信贷审批与风险评估三、支付与交易安全四、金融欺诈检测五、市场预测与情绪分析六、客户服务与个性化推荐七、面临的挑战与未来趋势八、总结 一、智能投顾与资产配置 智能投顾:通过机器学习技术&#…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...