【dbt】使用dbt对表格行转列

文章介绍了如何在dbt环境中使用Jinja语法和dbt的run_query()方法、dbt_utils.get_column_values()方法来实现动态SQL,特别是针对表格的行转列操作。通过创建测试表和模型,展示了遍历数组、查询字段唯一值以及利用dbt_utils包进行转换的不同方法。
摘要由CSDN通过智能技术生成

【dbt】使用dbt对表格行转列
使用dbtJinja 语法,可以实现动态SQL的效果,拼接出想要的SQL语句。以下是关于行转列的例子,使用了Jinja的变量、遍历语法,以及dbtrun_query()方法和 dbt_utils.get_column_values() 方法。

环境

  • 数据库:Postgres 14
  • dbt 1.5

生成测试表

  1. 创建源表模型 <dbt_project>/models/fct_score.sql
with source_data as (
    select 1 as id, '张三' as name, 'chinese' as class, 98 as score union all 
    select 2 as id, '张三' as name, 'math'    as class, 97 as score union all 
    select 3 as id, '张三' as name, 'english' as class, 96 as score union all 
    select 4 as id, '李四' as name, 'chinese' as class, 88 as score union all 
    select 5 as id, '李四' as name, 'math'    as class, 87 as score union all 
    select 6 as id, '李四' as name, 'english' as class, 86 as score 
)

select * from source_data
  1. 执行 dbt run -s fct_score,生成源表。
    在这里插入图片描述

实现行转列

class 字段的值转成列,有三种方法,其实都是用jinja生成需要的代码。
使用sqlclass字段的值转成列,我们用常使用以下sql

select name,
        sum(case when class='chinese' then score else 0 end) as "chinese" ,
        sum(case when class='math'    then score else 0 end) as "math" ,
        sum(case when class='english' then score else 0 end) as "english"
from "fct_score"
group by name
order by name
)

dbt中,可以利用 Jinja 语法构造出 class 课程的数组,再遍历数组的每个元素,生成 sum(...)代码。但需要注意,最后一个sum()语句末尾,不能带,符号。

一、遍历数组

创建行转列的表模型 ./models/test_r2c_1.sql

{#
    1. {% set 变量名=变量值 %}: 定义数组变量 class_name_list。
    2. 写出基本的SQL3. {{ for 元素名 in 数组名}}...{% endfor %}: 遍历数组, 用元素值构造出SQL4. {% if not loop.last %}, {% endif %}: 遍历到最后一个元素时,不加逗号。
#}

{% set class_name_list = ['chinese', 'math', 'english'] %}

with source_data as (
    select name, 
        {% for class_name in class_name_list %}
            sum(case when class='{{ class_name }}' then score else 0 end) as "{{ class_name }}" {% if not loop.last %}, {% endif %}
        {% endfor %}
    from {{ ref('fct_score') }}
    group by name 
    order by name 
)

select * from source_data 

运行模型dbt run -s test_r2c_1,生成结果表。
在这里插入图片描述

二、使用 run_query() 方法(不推荐)

实际工作时,往往需要转换的行值很多,比如上百个行值,甚至行值是不固定的。可以使用dbtrun_query(()方法,先查询出字段的唯一值,再赋值给变量后进行遍历。

  1. 定义变量,保存查询语句。
{% set class_name_query %}
    select distinct class from {{ ref('fct_score') }}
{% endset %}
  1. run_query() 方法执行查询。
{% set class_name_result = run_query(class_name_query) %}
  1. run_query() 返回结果是对象数组, 需要取对象的第0个元素才是所需要的值。
{% set class_name_list = class_name_result.columns[0].values() %}
  1. dbt compile阶段, run_query() 会返回 None 值,导致报错, 所以需要把 解析run_query()返回结果 的代码使用 {% if execute %}{% endif %} 包裹, 并赋一个空数组, 以避免报错。
{% if execute %}
    {% set class_name_list = class_name_result.columns[0].values() %}
{% else %}
    {% set class_name_list=[] %}
{% endif %}
  1. 遍历数组, 构造SQL,代码同方法一。
  2. 完整代码, 保存到 ./models/test_r2c_2.sql 模型中。
{% set class_name_query %}
    select distinct class from {{ ref('fct_score') }}
{% endset %}

{% set class_name_result = run_query(class_name_query) %}

{% if execute %}
    {% set class_name_list = class_name_result.columns[0].values() %}
{% else %}
    {% set class_name_list=[] %}
{% endif %}

with source_data as (
    select name, 
        {% for class_name in class_name_list %}
            sum(case when class='{{ class_name }}' then score else 0 end) as "{{ class_name }}" {% if not loop.last %}, {% endif %}
        {% endfor %}
    from {{ ref('fct_score') }}
    group by name 
    order by name 
)

select * from source_data 
  1. 运行模型 dbt run test_r2c_2 ,获得结果如下:
    在这里插入图片描述
    注意:使用 run_query() 方法时,dbt会在本地进行编译后执行,所以如果来源字段还有上家的话,可能会导致字段值变多。

使用 dbt_utils.get_column_values() 方法(推荐)

该方法也是通过查表获得需要转换的值,但比 run_query() 方法准确,推荐使用该方法。

  1. get_column_values()方法需要安装 dbt_utils 包,将以下内容 copy<dbt_project>/ 项目目录下的 packages.yml 文件中(没有该文件就新建一个),再执行 dbt deps 即可安装。
packages:
  - package: dbt-labs/dbt_utils
    version: 1.1.0
  1. 使用 get_column_values(table=ref('表名'), column='字段名') 即可查到指定字段的唯一值,并且以数组形式返回,因此使用很简单。
{% set class_name_list = dbt_utils.get_column_values(table=ref('fct_score'), column='class') %}
  1. 构造行转列的语句,同方法一。完整代码保存到 ./models/test_r2c_3.sql 模型中。
{% set class_name_list = dbt_utils.get_column_values(table=ref('fct_score'), column='class') %}

with source_data as (
    select name, 
        {% for class_name in class_name_list %}
            sum(case when class='{{ class_name }}' then score else 0 end) as "{{ class_name }}" {% if not loop.last %}, {% endif %}
        {% endfor %}
    from {{ ref('fct_score') }}
    group by name 
    order by name 
)

select * from source_data 
  1. 执行 dbt run -s test_r2c_3 ,得到结果如下:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值