Альтернативное решение dbt_utils.get_column_values() для модели dbt

Я пытался использовать переменную в модели dbt.

Однако я изо всех сил пытаюсь решить проблему, с которой столкнулся. Насколько я понимаю, dbt_utils.get_column_values() не работает с CTE, однако для решения этой проблемы я создаю вспомогательную модель, имитирующую CTE, и создаю таблицу.

Из этой таблицы (см. снимок ниже) я хочу получить максимальные уровни между строками, уровни будут разделены на /

поэтому для этого я создаю эту модель:

--/models/dim_product/stg/stg_aux_calculate.sql

select max(LENGTH(VALUE) - LENGTH(REPLACE(VALUE, '/', '')))::integer as max_splits
from {{ source('schema', 'category_tbl') }}

И это создаст следующую таблицу (с использованием снежинки):

Отсюда я создаю новую модель, которая хочет выбрать это значение 5 и установить его как переменную.

 -- will need to add 1 to account for the original string (tackle after)
 --/models/dim_product/stg/stg_category_model.sql

{%- set max_splits = dbt_utils.get_column_values(
    table = ref('stg_aux_calculate'), 
    column = 'max_splits')[0] -%}

with split_values as (
    select
        value_id,
        value,
        {% for i in range(1, max_splits) %}
            split_part(value, '/', {{ i }}) as value_component_{{ i }},
        {% endfor %}
        ATTRIBUTE_ID,
        ENTITY_ID,
        STORE_ID
    from {{ source('schema', 'category_tbl') }}
)

select
    value_id,
    value,
    {% for i in range(1, max_splits) %}
        max(value_component_{{ i }}) as value_component_{{ i }}{% if not loop.last %},{% endif %}
    {% endfor %}
    ,
    ATTRIBUTE_ID,
    ENTITY_ID,
    STORE_ID
from split_values
group by value_id,value, ATTRIBUTE_ID, ENTITY_ID, STORE_ID 

Я получаю ошибку TypeError: 'Undefined' object cannot be interpreted as an integer

Я знаю, что это работает, потому что если я жестко закодирую переменную с помощью:

{% set max_splits = 6 %}

Он вернет то, что я ожидаю:

На снежинке модель stg_aux_calculate.sql создается следующим образом.

    create or replace view DEV_etc.DBT_my_name_xyz.stg_aux_calculate(
    MAX_SPLITS
) as (
    select max(LENGTH(VALUE) - LENGTH(REPLACE(VALUE, '/', '')))::integer as max_splits
from DEV_RAW_INT
.conf.CATALOG_CATEGORY_conf
  );

Любое предложение по преодолению этого?

Через claire - get_column_values ​​несовместимо с CTE, поскольку оно основано на том, что объект, который вы передаете, является таблицей или представлением в базе данных. Вот обходной путь: напишите свой собственный макрос get_column_values_from_query, который делает то же самое, что и get_column_values, но не зависит от передачи таблицы или представления. См. discourse.getdbt.com/t/using-get-column-values-with-ctes/142‌​3

Bart McEndree 17.04.2024 15:58

ваш второй подход (имея отдельную модель и указывая ее в макросе) должен работать. Можете ли вы поделиться дополнительным кодом из этого? И еще, почему вы добавляете [0] в конце?

Aleix CC 18.04.2024 10:13

@AleixCC Я добавляю [0], чтобы быть уверенным, что выберу первое значение из max_splits, хотя модель aux_model генерирует только таблицу с 1 строкой и 1 столбцом с 1 значением. Если я не поставлю [0], я получу эту ошибку. TypeError: объект «список» не может быть интерпретирован как целое число, а если я поставлю [0], я получу ошибку TypeError: объект «Undefined» не может быть интерпретирован как целое число. Я также пытался использовать max_splits как целое число, но это не помогло. Идея состоит в том, чтобы использовать переменную с номером, полученным из aux_model, внутри цикла jinja. Я знаю, что это работает, если я жестко закодирую число (пример 6).

Pedro Gomes 18.04.2024 11:58

прохладный. но вам определенно нужно больше рассказать о том, где вы это используете set. Без этого помочь очень сложно: stackoverflow.com/help/minimal-reproducible-example

Aleix CC 18.04.2024 12:24

@AleixCC дополните мой пример дополнительной информацией, я считаю, что он более полный.

Pedro Gomes 18.04.2024 12:59
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
5
398
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я со своей стороны сделал несколько игрушечных рабочих примеров. Давайте посмотрим:

-- my_splits_model.sql // this will just return an integer

select 5 as max_splits
-- my_downstream_model.sql

{%- set max_splits = dbt_utils.get_column_values(
      table = ref('my_splits_model'),
      column = 'max_splits'
    )[0]
-%}

-- we want to make sure that the `max_splits` variable is an actual number
{% if max_splits is number %}
select 
    '{{ max_splits }}' as this_is_my_max_splits
  {% for split in range(1, max_splits) %}
    , 'split_number_{{ split }}' as split_{{ split }}
  {% endfor %}

{% else %}
select 'hmmm' as that_is_not_a_number

{% endif %}

Эта последняя модель компилируется в:

select
    '5' as this_is_my_max_splits
  
    , 'split_number_1' as split_1
  
    , 'split_number_2' as split_2
  
    , 'split_number_3' as split_3
  
    , 'split_number_4' as split_4

Если я обновлю my_splits_model.sql, чтобы вернуть что-то, кроме целого числа:

-- my_splits_model.sql // this will now not return an integer

select 'some text' as max_splits

Тогда my_downstream_model скомпилируется так:

select 'hmmm' as that_is_not_a_number

Итак, к вашему варианту использования: я бы просто убедился, используя встроенный тест Jinja is number, что то, что вы действительно получаете от max_splitsset, является реальным числом.

Благодаря вашим предложениям я смог обойти проблему. Удалил cte и построил 2 модели, а внутри модели всегда начинались с {% if max_splits is number %}, а в {% else %} я обновил тот же код, но с управляющей переменной c{% set control_value = 10 %}. Он делает то, что я хочу. Не очень понимаю, почему только внутри макроса, когда {% if max_splits is number %} он работает. Возможно, есть какие-то ограничения по dbt. Спасибо за помощь!

Pedro Gomes 19.04.2024 17:39

Хороший! К вашему сведению, вы также можете «принять» ответ от лица, задавшего вопрос, чтобы другие отметили, что это помогло решить вашу проблему :)

Aleix CC 22.04.2024 12:56

Другие вопросы по теме