Traffine I/O

Bahasa Indonesia

2022-12-30

Jinja dan makro

Pendahuluan

dbt memungkinkan Anda untuk menulis kode SQL yang fleksibel menggunakan Jinja dan makro. Artikel ini menjelaskan notasi dan penggunaan Jinja dan makro.

Jinja

Jinja adalah sejenis mesin template yang dapat digunakan untuk menulis tidak hanya SQL, tetapi juga HTML, CSS, dan banyak file teks lainnya.

Jinja menyediakan tiga notasi berikut.

  • {% ... %}: sintaks kontrol
    Mengontrol alur pemrosesan, seperti for atau if.
  • {{ ... }}: Ekspresi
    Mengeluarkan hasil dari ekspresi sebagai string.
  • {# ... #}: komentar
    Anda dapat menulis komentar untuk membuat program lebih mudah dibaca orang. Komentar-komentar tersebut tidak akan menjadi output pada SQL final.

Sintaks if

Jinja menggunakan sintaks if sebagai berikut.

{% if kenny.sick %}
    Kenny is sick.
{% elif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}

Jika sintaks if ditulis pada satu baris, maka akan terlihat seperti ini.

{{ env if env is defined else 'dev' }}

https://jinja.palletsprojects.com/en/3.1.x/templates/#if

Sintaks for

Di Jinja, sintaks for dijelaskan sebagai berikut.

{% for col in columuns %}
  {{ col }} ,
{% endfor %}

{% for key, val in dict %}
  {{ key }}, {{ val }}
{% endfor %}

Variabel khusus bisa digunakan dalam sintaks for.

Variabel Deskripsi
loop.index The current iteration of the loop. (1 indexed)
loop.index0 The current iteration of the loop. (0 indexed)
loop.revindex The number of iterations from the end of the loop (1 indexed)
loop.revindex0 The number of iterations from the end of the loop (0 indexed)
loop.first True if first iteration.
loop.last True if last iteration.
loop.length The number of items in the sequence.
loop.cycle A helper function to cycle between a list of sequences. See the explanation below.
loop.depth Indicates how deep in a recursive loop the rendering currently is. Starts at level 1
loop.depth0 Indicates how deep in a recursive loop the rendering currently is. Starts at level 0
loop.previtem The item from the previous iteration of the loop. Undefined during the first iteration.
loop.nextitem The item from the following iteration of the loop. Undefined during the last iteration.
loop.changed(*val) True if previously called with a different value (or not called at all).

https://jinja.palletsprojects.com/en/3.1.x/templates/#for

Fungsi-fungsi Jinja dbt sendiri

dbt mendefinisikan fungsi-fungsi Jinja-nya sendiri. Selain config(), ref(), dan source() yang umum digunakan, fungsi-fungsi berikut ini tersedia.

dbt Jinja functions
adapter execute run_query
as_bool flags run_started_at
as_native fromjson schema
as_number fromyaml schemas
as_text graph selected_resources
builtins invocation_id set
config log source
cross-database macros model statement blocks
dbt_project.yml Context modules target
dbt_version on-run-end Context this
debug print tojson
dispatch profiles.yml Context toyaml
doc project_name var
env_var ref zip
exceptions return

https://docs.getdbt.com/reference/dbt-jinja-functions

Contoh notasi Jinja

Misalkan Anda memiliki SQL berikut.

models/order_payment_method_amounts.sql
select
  order_id,
  sum(case when payment_method = 'bank_transfer' then amount end) as bank_transfer_amount,
  sum(case when payment_method = 'credit_card' then amount end) as credit_card_amount,
  sum(case when payment_method = 'gift_card' then amount end) as gift_card_amount,
sum(amount) as total_amount
from {{ ref('raw_payments') }}
group by 1

SQL di atas dapat ditulis menggunakan loop for Jinja dan variabel sebagai berikut.

models/order_payment_method_amounts.sql
{% 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,
  {% endfor %}
  sum(amount) as total_amount
from {{ ref('raw_payments') }}
group by 1

Gunakan loop.last untuk menghindari meletakkan koma di akhir perulangan for.

models/order_payment_method_amounts.sql
{% 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

Makro

Makro adalah fungsi-fungsi yang membuat kode yang dapat digunakan kembali. Tempatkan file SQL di direktori yang ditentukan oleh macro-paths di dbt_project.yml (macros secara default) dan tulis makro di dalamnya.

Misalnya, jika Anda ingin membuat daftar metode pembayaran sebagai variabel umum dalam makro, tulis sebagai berikut.

macros/get_payment_methods.sql
{% macro get_payment_methods() %}
{{ return(["bank_transfer", "credit_card", "gift_card"]) }}
{% endmacro %}
models/order_payment_method_amounts.sql
{% set payment_methods = get_payment_methods() %}

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

Jika Anda ingin menjadikan fungsi untuk mengkonversi mata uang sebagai fungsi umum dalam makro, tuliskan sebagai berikut.

macros/convert_currency.sql
{% macro usd_to_jpy(col_name, rate=100) %}
  {{col_name}} * rate
{% endmacro %}
models/orders.sql
select
  id,
  usd_to_jpy('price', 90)
from {{ ref('raw_payments') }}
group by 1

Referensi

https://docs.getdbt.com/docs/build/jinja-macros
https://docs.getdbt.com/reference/dbt-jinja-functions
https://jinja.palletsprojects.com/en/3.1.x/templates/#if
https://jinja.palletsprojects.com/en/3.1.x/templates/#for

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!