跳转到主要内容

在dbt模型中编写PRQL

项目描述

dbt-prql

dbt-prql 允许在dbt模型中编写PRQL。这结合了PRQL在查询中的强大功能和简单性,以及dbt在查询间的版本控制、血缘关系和测试。

安装 dbt-prql 后,dbt命令会将 {% prql %}{% endprql %} jinja标签之间的PRQL编译为SQL作为dbt编译的一部分。无需额外的配置。

示例

简单示例

{% prql %}
from employees
filter (age | in 20..30)
{% endprql %}

...将显示为dbt

SELECT
  employees.*
FROM
  employees
WHERE
  age BETWEEN 20
  AND 30

更复杂的示例

{% prql %}
from in_process = {{ source('salesforce', 'in_process') }}
derive expected_sales = probability * value
join {{ ref('team', 'team_sales') }} [name]
group name (
  aggregate (sum expected_sales)
)
{% endprql %}

...将显示为dbt

SELECT
  name,
  sum(in_process.probability * in_process.value) AS expected_sales
FROM
  {{ source('salesforce', 'in_process') }} AS in_process
  JOIN {{ ref('team', 'team_sales') }} USING(name)
GROUP BY
  name

...然后dbt将编译 sourceref 为完整的SQL查询。

替换宏

dbt对宏的使用为我们节省了许多代码行,甚至为一些人节省了一些时间。但是,使用像 if not loop.last 这样的代码进行文本生成的命令式编程并不是我们的最高目标。它是dbt的“必要”部分而不是“美观”部分。

这是dbt文档中宏的规范示例 dbt文档

{%- set payment_methods = ["bank_transfer", "credit_card", "gift_card"] -%}

select
order_id,
{%- for payment_method in payment_methods %}
sum(case when payment_method = '{{payment_method}}' then amount end) as {{payment_method}}_amount
{%- if not loop.last %},{% endif -%}
{% endfor %}
from {{ ref('raw_payments') }}
group by 1

这是使用PRQL(包括prql jinja标签)的模型

{% prql %}
func filter_amount method -> s"sum(case when payment_method = '{method}' then amount end) as {method}_amount"

from {{ ref('raw_payments') }}
group order_id (
  aggregate [
    filter_amount bank_transfer,
    filter_amount credit_card,
    filter_amount gift_card,
  ]
)
{% endprql %}

查询形式更加简单,使用PRQL编写还能在每次按键时获得关于任何错误的实时反馈。尽管还有很多要做的,但请查看PRQL Playground上的当前版本。

[^1]:请注意,当https://github.com/prql/prql/issues/82实现后,我们可以放弃s-string,并且可以选择性地放弃函数。

```elm
from {{ ref('raw_payments') }}
group order_id (
  aggregate [
    bank_transfer_amount = amount | filter payment_method == 'bank'        | sum,
    credit_card_amount = amount   | filter payment_method == 'credit_card' | sum,
    gift_amount = amount          | filter payment_method == 'gift_card'   | sum,
  ]
)
```

or

```elm
func filter_amount method -> amount | filter payment_method == method | sum

from {{ ref('raw_payments') }}
group order_id (
  aggregate [
    bank_transfer_amount = filter_amount 'bank'
    credit_card_amount   = filter_amount 'credit_card'
    gift_amount          = filter_amount 'gift_card'
  ]
)
```

它做什么

当dbt将模型编译成SQL查询时

  • 在dbt模型中,任何位于{% prql %}{% endprql %}标签之间的文本都会在传递给dbt之前从PRQL编译成SQL。
  • PRQL编译器将包含{{}}的文本直接传递给dbt而不会修改,这允许我们在PRQL中嵌入jinja表达式。(这专门为这个用例添加到PRQL中。)
  • 然后,dbt将编译得到的模型成最终的原始SQL形式,并将其按常规发送到数据库。

在dbt项目中不需要任何配置;这可以在安装了dbt-prql的任何dbt命令(例如dbt run)上自动工作。

安装

pip install dbt-prql

当前状态

目前这还是一个新功能,但功能相当完整。我们热情地支持它——如果有任何问题,请提交问题。

它是如何工作的?

遗憾的是,这是一种暗黑魔法。

dbt不允许在数据库适配器(例如dbt-bigquery)或仅jinja的插件(例如dbt-utils)之外添加行为。所以这个库通过黑客手段在Python启动时用额外的jinja扩展对dbt的jinja环境进行monkeypatch。

[^2]:感谢mtkennerly/poetry-dynamic-versioning的技术。

这个方法在与dbt团队这里这里讨论过。

这在不同版本的dbt之间不稳定,因为它依赖于内部dbt API。这种方法也是规范性不良的——它每次Python解释器启动时都会运行几行代码,这些错误可能会在问题域之外导致非常令人困惑的错误(尽管在这个库的情况下,它是小而精心构建的™)。

如果有任何担心这个库可能会引起问题,只需设置环境变量DBT_PRQL_DISABLE=1,这个库就不会进行monkeypatch。它也可以使用pip uninstall dbt-prql完全卸载。

路线图

欢迎提出想法;目前功能相当完整。如果我们不受dbt功能限制

  • 如果dbt允许外部插件,我们会热情地转向那里。
  • 我们希望这个功能能够在不包含{% prql %}标签的.prql文件上工作;但根据当前的方法,这需要相当侵入性的monkeypatch。
  • 如果我们能够自动添加方言(即prql dialect:snowflake),那么每个模型就可以节省一行。
  • 如果我们能够将其上流到dbt-core,那将是极好的。在那之前,PRQL可能需要先证明其持久性。

我们可能会把这个库移动到https://github.com/prql/PyPrqlhttps://github.com/prql/prql仓库。鉴于上面的黑客手段,我们更愿意将其作为单独的包保留,但并没有必要将其作为单独的仓库。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分发

dbt-prql-0.2.3.tar.gz (9.8 kB 查看哈希值)

上传时间

构建分发

dbt_prql-0.2.3-py3-none-any.whl (9.2 kB 查看哈希值)

上传时间 Python 3

由支持