Создание шаблонов SQL

Создание шаблонов SQL Lab


Шаблоны Джинджа(Jinja)

SQL Lab и режиме исследования(Explore) в Fincomtech Analytics поддерживают шаблонизацию Jinja в запросах. Чтобы включить шаблонизацию, необходимо установить ENABLE_TEMPLATE_PROCESSING флаг функции в файле конфигурации superset_config.py.

Если шаблонизация включена, код Python можно встраивать в виртуальные наборы данных и пользовательский SQL в элементах управления фильтрами и метриками в Explore. По умолчанию в контексте Jinja доступны следующие переменные:

  • columns: столбцы, по которым нужно сгруппировать данные в запросе
  • filter: фильтры, применяемые в запросе
  • from_dttm: начальное datetime значение из выбранного диапазона времени (None если не определено) (устарело в версии 5.0, вместо этого используйте get_time_filter)
  • to_dttm: конечное datetime значение из выбранного диапазона времени (None если не определено). (устарело, начиная с версии 5.0, используйте get_time_filter вместо этого)
  • groupby: столбцы, по которым группируются данные в запросе (устарело)
  • metrics: агрегатные выражения в запросе
  • row_limit: ограничение количества строк в запросе
  • row_offset: смещение строк в запросе
  • table_columns: столбцы, доступные в наборе данных
  • time_column: временной столбец в запросе (None если не определено)
  • time_grain: выбранное временное разрешение (None если не определено)

Например, чтобы добавить временной диапазон в виртуальный набор данных, можно написать следующее:

sqlSELECT *
FROM tbl
WHERE dttm_col > '{{ from_dttm }}' and dttm_col < '{{ to_dttm }}'

Вы также можете использовать логику Jinja, чтобы сделать запрос устойчивым к удалению фильтра timerange:

sqlSELECT *
FROM tbl
WHERE (
    {% if from_dttm is not none %}
        dttm_col > '{{ from_dttm }}' AND
    {% endif %}
    {% if to_dttm is not none %}
        dttm_col < '{{ to_dttm }}' AND
    {% endif %}
    1 = 1
)

1 = 1 в конце гарантирует, что значение будет присутствовать в предложении WHERE, даже если фильтр времени не установлен. Во многих системах управления базами данных это можно заменить на true.

Обратите внимание: параметры Jinja вызываются в двойных скобках в запросе и в одинарных скобках в логических блоках.

Чтобы добавить пользовательские функции в контекст Jinja, необходимо переопределить контекст Jinja по умолчанию в вашей среде, указав JINJA_CONTEXT_ADDONS в конфигурации Fincomtech Analytics (superset_config.py). Объекты, на которые есть ссылки в этом словаре, доступны пользователям там, где доступен контекст Jinja.

pythonJINJA_CONTEXT_ADDONS = {
    'my_crazy_macro': lambda x: x*2,
}

Значения по умолчанию для шаблонов Jinja можно указать в меню Parameters в пользовательском интерфейсе SQL Lab. В пользовательском интерфейсе можно задать набор параметров в формате JSON:

json{
  "my_table": "foo"
}

Параметры становятся доступными в вашем SQL-запросе (например: SELECT * FROM {{ my_table }}) благодаря использованию синтаксиса шаблонов Jinja. Параметры шаблона SQL Lab хранятся в наборе данных как TEMPLATE PARAMETERS.

Существует специальный параметр _filters, который можно использовать для тестирования фильтров, применяемых в шаблонах Jinja.

json{
  "_filters": [
    {
      "col": "action_type",
      "op": "IN",
      "val": ["sell", "buy"]
    }
  ]
}

Пример запроса:

sqlSELECT action, count(*) as times
FROM logs
WHERE action in {{ filter_values('action_type')|where_in }}
GROUP BY action

Обратите внимание, что _filters не сохраняется вместе с набором данных. Он используется только в пользовательском интерфейсе SQL Lab.

Пользовательские обработчики шаблонов

Помимо шаблонов Jinja по умолчанию, SQL Lab в Fincomtech Analytics также поддерживает пользовательские обработчики шаблонов, которые можно настроить с помощью CUSTOM_TEMPLATE_PROCESSORS в файле конфигурации. Значения в этом словаре переопределяют обработчики шаблонов Jinja по умолчанию для указанной базы данных.

В приведенном ниже примере настраивается пользовательский обработчик шаблонов Presto, который реализует собственную логику обработки макросов с помощью регулярных выражений. Он использует макрос $ вместо {{ }} в шаблонах Jinja.

При настройке с помощью CUSTOM_TEMPLATE_PROCESSORS шаблон SQL в базе данных Presto обрабатывается с помощью пользовательского шаблона, а не шаблона по умолчанию.

pythondef DATE(
    ts: datetime, day_offset: SupportsInt = 0, hour_offset: SupportsInt = 0
) -> str:
    """Current day as a string."""
    day_offset, hour_offset = int(day_offset), int(hour_offset)
    offset_day = (ts + timedelta(days=day_offset, hours=hour_offset)).date()
    return str(offset_day)

class CustomPrestoTemplateProcessor(PrestoTemplateProcessor):
    """A custom presto template processor."""

    engine = "presto"

    def process_template(self, sql: str, **kwargs) -> str:
        """Processes a sql template with $ style macro using regex."""
        # Add custom macros functions.
        macros = {
            "DATE": partial(DATE, datetime.utcnow())
        }  # type: Dict[str, Any]
        # Update with macros defined in context and kwargs.
        macros.update(self.context)
        macros.update(kwargs)

        def replacer(match):
            """Expand $ style macros with corresponding function calls."""
            macro_name, args_str = match.groups()
            args = [a.strip() for a in args_str.split(",")]
            if args == [""]:
                args = []
            f = macros[macro_name[1:]]
            return f(*args)

        macro_names = ["$" + name for name in macros.keys()]
        pattern = r"(%s)\s*\(([^()]*)\)" % "|".join(map(re.escape, macro_names))
        return re.sub(pattern, replacer, sql)

CUSTOM_TEMPLATE_PROCESSORS = {
    CustomPrestoTemplateProcessor.engine: CustomPrestoTemplateProcessor
}

SQL Lab также включает функцию проверки запросов в реальном времени с подключаемыми бэкендами. Вы можете настроить, какая реализация проверки будет использоваться с каким ядром базы данных, добавив в файл конфигурации следующий блок:

pythonFEATURE_FLAGS = {
    'SQL_VALIDATORS_BY_ENGINE': {
        'presto': 'PrestoDBSQLValidator',
    }
}


Доступные макросы

В этом разделе мы рассмотрим предустановленные макросы Jinja в Fincomtech Analytics.

Текущее имя пользователя

Макрос {{ current_username() }} возвращает username текущего пользователя, вошедшего в систему.

Если в конфигурации Fincomtech Analytics включено кэширование, то по умолчанию при вычислении ключа кэша Fincomtech Analytics будет использовать значение username. Ключ кэша — это уникальный идентификатор, который определяет, будет ли в будущем достигнут кэш и сможет ли система получить кэшированные данные.

Вы можете отключить включение значения username в расчет ключа кэша, добавив в код Jinja следующий параметр:
{{ current_username(add_to_cache_keys=False) }}

Текущий идентификатор пользователя

Макрос {{ current_user_id() }} возвращает идентификатор учетной записи пользователя, вошедшего в систему в данный момент.

Если в конфигурации Fincomtech Analytics включено кэширование, то по умолчанию при вычислении ключа кэша id система будет использовать значение account. Вы можете отключить включение значения account id в расчет ключа кэша:
{{ current_user_id(add_to_cache_keys=False) }}

Адрес электронной почты текущего пользователя

Макрос {{ current_user_email() }} возвращает адрес электронной почты пользователя.

Если включено кэширование, Fincomtech Analytics будет использовать email в ключе кэша. Отключить это можно так:
{{ current_user_email(add_to_cache_keys=False) }}

Текущие роли пользователя

Макрос {{ current_user_roles() }} возвращает массив ролей вошедшего в систему пользователя.

Если включено кэширование, Fincomtech Analytics будет использовать значение roles в ключе кэша. Отключить это можно так:
{{ current_user_roles(add_to_cache_keys=False) }}

Вы можете преобразовать массив в JSON, добавив |tojson:
{{ current_user_roles()|tojson }}

Пример использования с фильтром |where_in в SQL-запросе:

sqlSELECT * FROM users WHERE role IN {{ current_user_roles()|where_in }}

Если роли ['admin', 'viewer'], запрос будет:

sqlSELECT * FROM users WHERE role IN ('admin', 'viewer')

Текущие пользовательские правила RLS

Макрос {{ current_user_rls_rules() }} возвращает массив правил RLS (Row Level Security), применяемых к текущему набору данных для вошедшего в систему пользователя.

Пользовательские параметры URL

Макрос {{ url_param('custom_variable') }} позволяет задавать произвольные параметры URL и ссылаться на них в коде SQL.

Вот конкретный пример:
В SQL Lab вы пишете следующий запрос:

sqlSELECT count(*)
FROM ORDERS
WHERE country_code = '{{ url_param('countrycode') }}'

К примеру Вы размещаете Fincomtech Analytics на домене https://analitic.fincom.tech отправляете коллегам ссылки:


Для коллеги из Белоруссии запрос преобразуется в:

sqlSELECT count(*) FROM ORDERS WHERE country_code = 'BY'

Явное включение значений в ключ кэша

Функция {{ cache_key_wrapper() }} явно указывает Fincomtech Analytics добавить значение в накопленный список значений, используемых при вычислении ключа кэша.
Эта функция нужна только в том случае, если вы хотите обернуть возвращаемые значения собственной пользовательской функции в ключ кэша. Обратите внимание, что эта функция обеспечивает кэширование значений user_id и username в вызовах функций current_user_id() и current_username() (если у вас включено кэширование).

Значения фильтров

Вы можете получить список значений для определенного фильтра с помощью {{ filter_values() }}.
Это полезно, если вы хотите фильтровать запрос по столбцу, имя которого отличается от имени в операторе выбора, или для повышения производительности через фильтрацию во внутреннем запросе.

Пример:

sqlSELECT action, count(*) as times
FROM logs
WHERE
    action in {{ filter_values('action_type')|where_in }}
GROUP BY action

Фильтры для конкретного столбца

Макрос {{ get_filters() }} возвращает фильтры, применённые к заданному столбцу. Помимо значений (как filter_values()), макрос get_filters() возвращает оператор, указанный в интерфейсе Explore.

Пример использования для сложной логики фильтрации:

sqlWITH RECURSIVE superiors(...) AS (
    SELECT ...
    FROM employees
    WHERE 1=1
    {# Render a blank line #}
    {%- for filter in get_filters('full_name', remove_filter=True) -%}
        {%- if filter.get('op') == 'IN' -%}
            AND full_name IN {{ filter.get('val')|where_in }}
        {%- endif -%}
        {%- if filter.get('op') == 'LIKE' -%}
            AND full_name LIKE {{ "'" + filter.get('val') + "'" }}
        {%- endif -%}
    {%- endfor -%}
    UNION ALL
    ...
)
SELECT ... FROM superiors

Временной Фильтр

Макрос {{ get_time_filter() }} возвращает временной фильтр, применённый к определённому столбцу. Это позволяет применять временные фильтры внутри виртуального набора данных (внутреннего запроса) для оптимизации производительности.

Макрос принимает следующие параметры:

  • column: Имя временного столбца.
  • default: Значение по умолчанию (например, "Last week").
  • target_type: Целевой временной тип БД (TIMESTAMP, DATE).
  • strftime: Формат времени (Python strftime).
  • remove_filter: Если true, фильтр удаляется из внешнего запроса.

Тип возвращаемого значения имеет свойства: from_expr, to_expr, time_range.

Пример использования таблицы logs из хранилища метаданных Fincomtech Analytics:

sql{% set time_filter = get_time_filter("dttm", remove_filter=True) %}
{% set from_expr = time_filter.from_expr %}
{% set to_expr = time_filter.to_expr %}
{% set time_range = time_filter.time_range %}
SELECT
  *,
  '{{ time_range }}' as time_range
FROM logs
{% if from_expr or to_expr %}WHERE 1 = 1
{% if from_expr %}AND dttm >= {{ from_expr }}{% endif %}
{% if to_expr %}AND dttm < {{ to_expr }}{% endif %}
{% endif %}

Использование параметра default:

sql{% set time_filter = get_time_filter("dttm", default="Last week", remove_filter=True) %}
SELECT
  *,
  '{{ time_filter.time_range }}' as time_range
FROM logs
WHERE
  dttm >= {{ time_filter.from_expr }}
  AND dttm < {{ time_filter.to_expr }}

Наборы данных

Физические и виртуальные наборы данных можно запрашивать с помощью макроса dataset. Это удобно для повторного использования определений метрик и вычисляемых столбцов.

Чтобы использовать макрос, найдите ID набора данных в его URL (например, dataset_id=42).
Запрос данных:

sqlSELECT * FROM {{ dataset(42) }} LIMIT 10

Включение метрик (агрегированных данных):

sqlSELECT * FROM {{ dataset(42, include_metrics=True) }} LIMIT 10

Группировка по подмножеству столбцов:

sqlSELECT * FROM {{ dataset(42, include_metrics=True, columns=["ds", "category"]) }} LIMIT 10

Метрики

Макрос {{ metric('metric_key', dataset_id) }} можно использовать для получения синтаксиса SQL для метрик из набора данных.

Это позволяет:

  1. Переопределять метки метрик.
  2. Объединять метрики в расчетах.
  3. Использовать синтаксис метрики в SQL Lab.
  4. Повторно использовать метрики в разных наборах данных.

Параметр dataset_id необязателен; если не указан, Fincomtech Analytics будет использовать текущий набор данных из контекста.