カスタムclean roomテンプレートの作成

clean roomテンプレートについて

Clean roomテンプレートは JinjaSQL で記述されています。JinjaSQL はJinjaテンプレート言語の拡張機能で、 SQL クエリを出力として生成します。JinjaSQL はロジックステートメントと実行時の変数解決をサポートするため、ユーザーは実行時にクエリをカスタマイズできます。変数は通常、テンプレートで使用され、クエリで使用するテーブル名、テーブル列、カスタム値を変数で指定できます。

Snowflakeには、一般的なユースケースに対応した事前設計のテンプレートが用意されています。これらのストックテンプレートは、ウェブアプリケーション以外では使用できませんが、プロバイダーもコンシューマーも、clean roomのカスタムテンプレートを作成できます。カスタムテンプレートはコーディングでのみ作成可能ですが、実行はコードからでも、ウェブアプリからでも可能です。

テンプレートには大きく分けて2つのタイプがあります:

  • 分析テンプレート は、 SELECT ステートメント (または、 SELECT 演算セット) を評価します。

  • アクティベーションテンプレート は、 SELECT ステートメントを CREATE TABLE ステートメントの入れ子にして評価し、テーブル名を返します。このテンプレートは、clean roomの構成に応じて、コンシューマーまたはプロバイダーのSnowflakeアカウントまたはサードパーティにエクスポートされるデータを生成します。アクティビティテンプレートは、分析テンプレートとよく似ていますが、 いくつかの追加要件があります

clean room UI で、分析テンプレートをアクティベーションテンプレートと関連付けることで、呼び出し元は分析を実行し、その後に自分自身またはサードパーティにデータを送信できます。アクティベーションテンプレートを、関連する分析テンプレートと同じクエリに解決する必要はありません。

カスタムテンプレートの作成と実行

デフォルト設定のclean roomで、プロバイダーはclean roomにテンプレートを追加し、コンシューマーはそれを選択、構成、実行することができます:

  1. プロバイダー はカスタムテンプレートを設計し、 provider.add_custom_sql_template を呼び出すことでclean roomに追加します。

  2. コンシューマーconsumer.run_analysis を呼び出てプロバイダーのテンプレートを実行し、テンプレートが必要とする変数の値を渡します。

このフローでは、コンシューマーはプロバイダーからclean roomに招待される必要がありますが、それ以外にサードパーティからの許可は必要ありません。このプロセスには、コンシューマーが提供するテンプレートやプロバイダーが実行するテンプレートなどのバリエーションがありますが、 ここでは説明しません

データ保護

テンプレートはプロバイダーとコンシューマーがclean roomにリンクしたデータセットにのみアクセスできます。

データプロバイダーとコンシューマーの両方が、結合、列、およびアクティビティポリシーをデータに設定でき、結合列、投影列、またはアクティブ化された結果に投影する列を保護することができます。

簡単な例

以下は、プロバイダーテーブルとコンシューマーテーブルをメールで結合し、重複回数を都市ごとに表示するシンプルな SQL の例です:

SELECT COUNT(*), city FROM consumer_table
  INNER JOIN provider_table
  ON consumer_table.hashed_email = provider_table.hashed_email
  GROUP BY city;
Copy

以下のクエリは、呼び出し元による列およびテーブルの選択/グループ化、結合の選択を許可するテンプレートの例です:

SELECT COUNT(*), IDENTIFIER({{ group_by_col | column_policy }})
  FROM IDENTIFIER({{ my_table[0] }}) AS C
  INNER JOIN IDENTIFIER({{ source_table[0] }}) AS P
  ON IDENTIFIER({{ consumer_join_col | join_policy }}) = IDENTIFIER({{ provider_join_col | join_policy }})
  GROUP BY IDENTIFIER({{ group_by_col | column_policy }});
Copy

このテンプレートの注意事項:

  • {{ double bracket pairs }} 内の値はカスタム変数です。 group_by_colmy_tablesource_tableconsumer_join_colprovider_join_colgroup_by_col はすべて、呼び出し元が設定したカスタム変数です。

  • source_table および my_table は、呼び出し元が設定するSnowflake定義の文字列配列変数です。配列のメンバーは、clean roomにリンクされたプロバイダーとコンシューマーテーブルの完全修飾名です。呼び出し元は、各配列にどのテーブルを含めるかを指定します。

  • テンプレートでは、プロバイダーテーブルのエイリアスを P に、コンシューマーテーブルのエイリアスを C にする必要があります。複数のテーブルがある場合は、 P1P2C1C2 のようにインデックスを作成します。

  • IDENTIFIER がすべての列名とテーブル名に必要です。なぜなら、 {{ double brackets }} の変数は文字列リテラルに評価されますが、これは有効な識別子ではないからです。

  • JinjaSQL フィルター を変数に適用できます。Snowflakeが実装する join_policycolumn_policy のカスタムフィルターは、列がclean roomの結合ポリシーまたは列ポリシーに準拠しているかを検証し、準拠していない場合はクエリが失敗します。フィルターは、 {{ column_name | filter_name }} のように列名に適用されます。

これらの点については、後で詳しく説明します。

コンシューマーがこのテンプレートを実行するコードを以下に示します。列名がテンプレートで宣言されたテーブルエイリアスによって修飾される点に注目してください。

CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.CONSUMER.RUN_ANALYSIS(
  $cleanroom_name,
  $template_name,
  ['my_db.my_sch.consumer_table],       -- Populates the my_table variable
  ['my_db.my_sch.provider_table'],      -- Populates the source_table variable
  OBJECT_CONSTRUCT(                     -- Populates custom named variables
    'consumer_join_col','c.age_band',
    'provider_join_col','p.age_band',
    'group_by_col','p.device_type'
  )
);
Copy

このテンプレートをウェブアプリケーションで使用するために、プロバイダーは テンプレートのカスタム UI フォームを作成することが必要になります。UI フォームには、テンプレートの変数名に対応する名前付きフォーム要素があり、フォームに指定された値がテンプレートに渡されます。

Tip

開発の初期段階で consumer.get_sql_jinja プロシージャを使用する、テンプレートがレンダリングされるとどのように表示されるかを確認できます。しかし、このプロシージャは join_policy などのclean roomフィルター拡張に対応していないため、プロシージャに送られるテンプレートでは、それらのフィルターを省略する必要があります。

カスタムテンプレートの構文

Snowflake Data Clean Roomsは、いくつか機能が拡張されたV3の JinjaSQL をサポートしています。

このセクションには以下のトピックが含まれます:

テンプレートの命名規則

テンプレートを作成する場合、名前は小文字、数字、スペース、またはアンダースコアのみを使用する必要があります。アクティベーションテンプレート (コンシューマーが実行するプロバイダーのアクティベーションを除く) の名前は、先頭が「activation」である必要があります。 provider.add_custom_sql_template または consumer.create_template_request を呼び出すと、テンプレート名が割り当てられます。

有効な名前の例:

  • my_template

  • activation_template_1

無効な名前の例:

  • my template - スペースは使用できません

  • My_Template - 小文字のみ使用可

テンプレート変数

テンプレートの呼び出し元は、テンプレート変数に値を渡すことができます。JinjaSQL 構文を使用すると、 {{ double_brackets }} 内の任意の変数名に対する変数バインディングが可能になりますが、Snowflakeは以下に説明するように、変数名をいくつか予約しており、それらをオーバーライドすることはできません。

注意

Snowflake定義の変数でも、カスタム変数でも、すべての変数をユーザーが入力するため、注意して適切に操作する必要があります。Snowflake Data Clean Roomsテンプレートは必ず、単一の SELECT ステートメントに解決されますが、すべての変数が呼び出し元から渡されることを覚えておく必要があります。

Snowflake定義の変数

すべてのclean roomテンプレートは、Snowflakeによって定義される以下のグローバル変数にアクセスできますが、それらは呼び出し元から渡されます。

source_table:

clean roomでプロバイダーがリンクしたテーブルとビューの、ゼロベースの文字列配列。テンプレートで使用します。テーブル名は次のように完全修飾されます: my_db.my_sch.provider_customers

例: SELECT col1 FROM IDENTIFIER({{ source_table[0] }}) AS p;

my_table:

clean roomのコンシューマーテーブルとビューの、ゼロベースの文字列配列。テンプレートで使用使用します。テーブル名は次のように完全修飾されます: my_db.my_sch.consumer_customers

例: SELECT col1 FROM IDENTIFIER({{ my_table[0] }}) AS c;

privacy:

ユーザーとテンプレートに関連付けられた、プライバシー関連の値のセット。 使用可能な子フィールドのリストをご参照ください。これらの値を、ユーザーに対して 明示的に設定 できますが、設定されていない場合もあるため、テンプレートでは常にデフォルト値を指定する必要があります。 privacy.threshold のように、テンプレートの子フィールドに直接アクセスしてください。

例: threshold_value を使用して集約句に最小グループサイズを適用するテンプレートのコード例を以下に示します。

SELECT
  IFF(a.overlap > ( {{ privacy.threshold_value | default(2)  | sqlsafe }} ),
                    a.overlap,1 ) AS overlap,
  c.total_count AS total_count
  ...
Copy

注釈

レガシーなclean roomグローバル変数には、 measure_columnsdimensions の2つがあります。これらを使用することは現在推奨されていませんが、レガシーなテンプレートやドキュメントには定義が残っている場合があります。名前の衝突を避けるため、これらの名前を使用してテーブルや列のエイリアスを作成しないでください。

カスタム変数

テンプレートの作成者は、呼び出し元が入力する任意の変数をテンプレートに含めることができます。これらの変数に、Jinja に準拠した任意の名前を(Snowflake定義の変数やテーブルのエイリアス名を除いて)付けることができます。テンプレートをウェブアプリケーションで使用したい場合は、ウェブアプリのユーザー向けに UI フォームも作成する必要があります。API ユーザー向けには、必須の変数とオプション変数に関するドキュメントをわかりやすく作成してください。

カスタム変数は、以下に示すカスタム変数 max_income のように、テンプレートからアクセスできます。

SELECT income FROM my_db.my_sch.customers WHERE income < {{ max_income }};
Copy

ユーザーは2種類の方法で、変数をテンプレートに渡すことができます。

  • ウェブアプリの場合は、 テンプレート開発者が作成した UI フォームから、値を選択または入力します。この UI フォームには、ユーザーがテンプレートに値を入力するためのフォーム要素が含まれています。フォーム要素の名前が変数名になります。テンプレートは単に、フォーム要素の名前を使用して値にアクセスします。UI フォームを作成するには、 provider.add_ui_form_customizations を使用します。

  • コードの場合は、 コンシューマーが consumer.run_analysis を呼び出し、引数配列のテーブル名、および名前と値のペアであるカスタム変数を、 analysis_arguments 引数に渡します。

注釈

clean roomにアップロードされたカスタムなPythonコードで、ユーザーが入力する値にアクセスする必要がある場合は、Python 関数の引数を使用して、 変数の値を明示的に コードに渡す必要があります。 {{jinja variable binding syntax}} を使用し、pythonコード内で直接テンプレート変数にアクセスすることはできません。

変数を正しく解決する

文字列値はテンプレートに渡されると、最終的なテンプレートで文字列リテラルに解決されます。バインドされた変数の処理が適切出ない場合、 SQL 解析エラーまたは論理エラーの原因になります:

  • SELECT {{ my_col }} FROM P;SELECT 'my_col' from P; に解決されます。これは単純に文字列 "my_col" を返すだけであり、あまり役に立ちません。

  • SELECT age FROM {{ my_table[0] }} AS P;SELECT age FROM 'somedb.somesch.my_table' AS P; に評価されると、テーブルはリテラル文字列ではなく識別子でなければならないため、解析エラーが発生します。

  • “WHERE age < 50” に渡される SELECT age FROM IDENTIFIER({{ my_table[0] }}) AS P {{ where_clause }};SELECT age FROM mytable AS P 'WHERE age < 50'; に評価されると、リテラル文字列の WHERE 句により解析エラーが発生します。

したがって、必要に応じて変数を解決する必要があります。次では、テンプレートで変数を適切に解決する方法を説明します:

テーブルと列の名前

テーブル名や列名を指定する変数は、以下の2つの方法のどちらかを使用し、テンプレート内で識別子に変換する必要があります:

  • IDENTIFIER: 例: SELECT IDENTIFIER({{ my_column }}) FROM P;

  • sqlsafe: この JinjaSQL フィルターは識別子の文字列を SQL テキストに解決します。前項と同等のステートメントは SELECT {{ my_column | sqlsafe }} FROM P; です

IDENTIFIER と sqlsafe のどちらを使うかは、状況によって異なります。たとえば、 c.{{ my_column | sqlsafe }} は、 IDENTIFIER を使用して簡単に書き換えることはできません。

動的 SQL

WHERE 句のように、リテラル SQL として使用する文字列変数がある場合は、 sqlsafe フィルターをテンプレートで使用してください。例:

SELECT age FROM IDENTIFIER({{ my_table[0] }}) AS C WHERE {{ where_clause }};
Copy

ユーザーが "age< 50 "を where_clause に渡した場合、このクエリは SELECT age FROM sometable AS C WHERE 'age < 50'; に解決されますが、リテラル文字列の WHERE 条件により、無効な SQL になります。この場合は、 sqlsafe フィルターを使用する必要があります:

SELECT age FROM IDENTIFIER( {{ my_table[0] }} ) as C {{ where_clause | sqlsafe }};
Copy

必須のテーブルエイリアス

クエリのトップレベルでは、Snowflakeがクエリ内で結合および列のポリシーを正しく検証するために、すべてのテーブルまたはサブクエリのエイリアスを P (プロバイダーテーブルの場合) または C (コンシューマーテーブルの場合) として作成する必要があります。結合または列のポリシーに対して検証が必要な列は、 P または C でエイリアスされたテーブルに含まれています。(PC を指定することで、プロバイダーとコンシューマーのどちらのポリシーで列を検証するかを、バックエンドが判断できます)。

クエリで複数のプロバイダーまたはコンシューマーテーブルを使用する場合は、初回以降、各テーブルのエイリアスに1から始まるシーケンシャルな接尾辞を追加します。つまり、プロバイダーテーブルの場合は、初回から3回目まで PP1P2 となり、コンシューマーテーブルの場合は、初回から3回目まで CC1C2 となります。 P または C のインデックスは、隙間なく連続している必要があります (つまり、 PP2P4 ではなく、 PP1P2 のようにエイリアスを作成します)。

SELECT col1 FROM IDENTIFIER({{ source_table[0] }}) AS P;
Copy

テンプレートフィルター

Snowflake は、すべての 標準 Jinja フィルター と、ほとんどの標準 JinjaSQL フィルター、およびいくつかの拡張機能をサポートしています:

  • join_policy: 列がテーブルの結合ポリシーで許可されているかを検証し、許可されていない場合は失敗します。

  • column_policy: 列がテンプレートの列ポリシーで許可されているか (投影が許可されているか) を検証します。

  • activation_policy: フィルターされた列がclean roomのアクティベーション ポリシー (provider.set_activation_policy または consumer.set_activation_policy) で許可されているかを検証します。

  • join_and_column_policy: 列が結合、アクティベーション、または列のポリシーで許可されているかを検証します。clean roomの柔軟性を高めるために使用します。コラボレーターはテンプレートを変更することなく、結合や列のポリシーを更新できるようになります。

  • identifier JinjaSQL フィルターは、Snowflakeテンプレートでは サポートされていません

JinjaSQL フィルターは左から右に分析されます:

  • {{ my_col | column_policy }} 正しい

  • {{ my_col | sqlsafe | column_policy }} 正しい

  • {{ column_policy | my_col }} 正しくない

  • {{ my_col | column_policy | sqlsafe }} 正しくない

clean roomへのポリシーの適用

Clean roomは、テンプレートで使用する列に対するclean roomポリシーのチェックを、自動では行いません。ポリシーを列に適用したい場合は、適切な ポリシーフィルター を、テンプレートのその列に適用する必要があります。例:

JOIN IDENTIFIER({{ source_table[0] }}) AS p
  ON {{ c_join_col | sqlsafe | join_policy }} = {{ p_join_col | sqlsafe }}
Copy

これは、 c_join_col に渡された列に対して結合ポリシーのテストを行いますが、 p_join_col に対するテストはしません。

ポリシーをテストする際、列名を曖昧にはできないので注意してください。これは他の SQL 使用時でも同じです。したがって、2つのテーブルに同じ名前の列がある場合、その列に対してポリシーをテストするには、列名を修飾する必要があります。

カスタムなPythonコードの実行

clean roomにアップロードされたPythonコードを、テンプレートから実行することができます。テンプレートからPython関数を呼び出し、データ行から値を受け取ったり、クエリで使用する値や投影する値を返したりできます。

  • プロバイダー がカスタムなPythonコードをclean roomにアップロードすると、テンプレートは構文 cleanroom.function_name を使用してPython関数を呼び出します。 詳細情報。

  • コンシューマー がカスタムなPythonコードをclean roomにアップロードすると、テンプレートは consumer.generate_python_request_template に渡された素の function_name の値を使用して関数を呼び出します (プロバイダーコードのように clean room にスコープされることはありません)。 詳細情報。

プロバイダーコードの例:

-- Provider uploads a Python function that takes two numbers and returns the sum.
call samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
  $cleanroom_name,
  'simple_addition',                        -- Function name to use in the template
  ['someval integer', 'added_val integer'], -- Arguments
  [],                                       -- No packages needed
  'integer',                                -- Return type
  'main',                                   -- Handler for function name
  $$

def main(input, added_val):
  return input + int(added_val)
    $$
);

-- Template passes value from each row to the function, along with a
-- caller-supplied argument named 'increment'
call samooha_by_snowflake_local_db.provider.add_custom_sql_template(
    $cleanroom_name,
    'simple_python_example',
$$
    SELECT val, cleanroom.simple_addition(val, {{ increment | sqlsafe }})
    FROM VALUES (5),(8),(12),(39) AS P(val);
$$
);
Copy

セキュリティに関する考慮事項

テンプレートはclean roomのネイティブアプリケーションが実行する単一の SELECT クエリによって評価される必要があります。現在のユーザーのIDでは、テンプレートは実行されません。

ユーザーはclean room内のデータに直接アクセスするのではなく、すべてのアクセスがテンプレート結果を介して、ネイティブアプリケーションで行われます。

テンプレートの中で明示的に列名を定義した場合でも、列やテーブルがプロバイダーによって提供される場合でも、 クエリで列が使用されると必ず、ポリシーフィルターが適用されます。 結合や列のポリシーを後から変更したり、列を変更したりした際に、テンプレートの更新を忘れないでください。ユーザーが入力した列に対し、 join_policycolumn_policyjoin_and_column_policyactivation_policy のフィルターを適用してください。

アクティベーションテンプレート

テンプレートを使用すると、クエリ結果をclean room外のテーブルに保存することもできます。これを アクティベーション と呼びます。現在、カスタムテンプレートでサポートされているアクティベーションは、プロバイダーのアクティベーションと、コンシューマーのアクティベーション (それぞれプロバイダーまたはコンシューマーのSnowflakeアカウントに結果を保存します) のみです。 アクティベーションの実装方法についてはこちらをご参照ください。

アクティベーションテンプレートは、以下の要件が追加された分析テンプレートです:

  • アクティベーションテンプレートは、 SQL スクリプトブロックに評価される JinjaSQL ステートメントで、単純な SELECT ステートメントになる分析テンプレートとは異なります。

  • アクティベーション テンプレート の名前は、文字列 activation で始まらなければなりません (コンシューマーが実行するプロバイダーアクティベーションテンプレートを除く)。例: activation_my_template

  • アクティベーションテンプレートには テーブル を作成する必要があります。テーブル名はアクティベーションの種類によって異なります:

    • プロバイダーが実行するプロバイダーアクティベーション: 生成されるテーブル名を cleanroom.temp_result_data にする必要があります。

    • その他すべてのアクティベーションタイプ: 生成されるテーブル名にプレフィックス cleanroom.activation_data_ を付ける必要があります (例: cleanroom.activation_data_cross_activation_results)。テーブル名は、clean room内で一意でなければなりません。

    生成されるテーブルは中間テーブルです。直接アクセスすることは避けてください。

  • スクリプトブロックの最後には RETURN ステートメントを記述します。これは、生成されたテーブルの名前をから cleanroom.cleanroom.activation_data_ のプレフィックスを除いた名前を返します。

  • アクティベートされる列は、データをリンクしたプロバイダーまたはコンシューマーの アクティベーションポリシー にリストされ、かつ activation_policy フィルターが適用されている必要があります。列はアクティベーション列の場合もあれば、結合列の場合もあります。

  • テンプレートをclean room UI から実行する場合は、 ウェブフォームを用意 し、そこに activation_template_nameenabled_activations のフィールドを含める必要があります。UI で使用するテンプレートには、分析テンプレートと、関連するアクティビティテンプレートが必要です。

  • テーブルが生成されるため、すべての計算列は推測される名前ではなく、明示的にエイリアスされなければなりません。つまり:

    SELECT COUNT(*), P.status from T AS P; FAILS、なぜなら、 COUNT 列名が推測されるからです。

    SELECT COUNT(*) AS COUNT_OF_ITEMS, P.status from T AS P; SUCCEEDS、なぜなら、 COUNT 列を明示的にエイリアスしているからです。

以下に、基本的なアクティベーションテンプレートの例を2つ示します。1つはプロバイダーが実行するサーバーのアクティベーションで使用し、もう1つはその他のアクティベーションタイプで使用します。両者の違いは、結果テーブルの名前を含む強調表示された2行です。

-- These are the required table name strings.
BEGIN
  CREATE OR REPLACE TABLE cleanroom.temp_result_data AS
    SELECT COUNT(c.status) AS ITEM_COUNT, c.status, c.age_band
      FROM IDENTIFIER({{ my_table[0] }}) AS c
    JOIN IDENTIFIER({{ source_table[0] }}) AS p
      ON {{ c_join_col | sqlsafe | activation_policy }} = {{ p_join_col | sqlsafe | activation_policy }}
    GROUP BY c.status, c.age_band
    ORDER BY c.age_band;
  RETURN 'temp_result_data';
END;
Copy

次のステップ

テンプレートシステムの説明は以上です。以下では、clean roomの実装についてテンプレートタイプ別に説明します:

  • プロバイダーテンプレート は、プロバイダーが作成するテンプレートです。これがデフォルトのユースケースです。

  • コンシューマーテンプレート コンシューマーが作成するテンプレートです。場合によっては、clean roomの作成者が、自身のテンプレートをclean roomに作成、アップロード、実行することをコンシューマーに許可したい場合があります。

  • アクティベーションテンプレート 実行に成功すると結果テーブルが作成されます。アクティベーションテンプレートに応じて、結果テーブルはclean room外のプロバイダーまたはコンシューマーのアカウントに保存されることも、アクティベーションハブにリストされたサードパーティのアクティベーションプロバイダーに送信されることもあります。

  • 連鎖テンプレート 複数のテンプレートを連鎖させて、各テンプレートの出力を次のテンプレートで使用することができます。

詳細情報