カスタム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にテンプレートを追加し、コンシューマーはそれを選択、構成、実行することができます:
プロバイダー はカスタムテンプレートを設計し、
provider.add_custom_sql_template
を呼び出すことでclean roomに追加します。コンシューマー は
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;
以下のクエリは、呼び出し元による列およびテーブルの選択/グループ化、結合の選択を許可するテンプレートの例です:
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 }});
このテンプレートの注意事項:
{{ double bracket pairs }} 内の値はカスタム変数です。
group_by_col
、my_table
、source_table
、consumer_join_col
、provider_join_col
、group_by_col
はすべて、呼び出し元が設定したカスタム変数です。source_table
およびmy_table
は、呼び出し元が設定するSnowflake定義の文字列配列変数です。配列のメンバーは、clean roomにリンクされたプロバイダーとコンシューマーテーブルの完全修飾名です。呼び出し元は、各配列にどのテーブルを含めるかを指定します。テンプレートでは、プロバイダーテーブルのエイリアスを
P
に、コンシューマーテーブルのエイリアスをC
にする必要があります。複数のテーブルがある場合は、P1
、P2
、C1
、C2
のようにインデックスを作成します。IDENTIFIER がすべての列名とテーブル名に必要です。なぜなら、 {{ double brackets }} の変数は文字列リテラルに評価されますが、これは有効な識別子ではないからです。
JinjaSQL フィルター を変数に適用できます。Snowflakeが実装する
join_policy
とcolumn_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'
)
);
このテンプレートをウェブアプリケーションで使用するために、プロバイダーは テンプレートのカスタム 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 ...
注釈
レガシーなclean roomグローバル変数には、 measure_columns
と dimensions
の2つがあります。これらを使用することは現在推奨されていませんが、レガシーなテンプレートやドキュメントには定義が残っている場合があります。名前の衝突を避けるため、これらの名前を使用してテーブルや列のエイリアスを作成しないでください。
カスタム変数¶
テンプレートの作成者は、呼び出し元が入力する任意の変数をテンプレートに含めることができます。これらの変数に、Jinja に準拠した任意の名前を(Snowflake定義の変数やテーブルのエイリアス名を除いて)付けることができます。テンプレートをウェブアプリケーションで使用したい場合は、ウェブアプリのユーザー向けに UI フォームも作成する必要があります。API ユーザー向けには、必須の変数とオプション変数に関するドキュメントをわかりやすく作成してください。
カスタム変数は、以下に示すカスタム変数 max_income
のように、テンプレートからアクセスできます。
SELECT income FROM my_db.my_sch.customers WHERE income < {{ max_income }};
ユーザーは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 }};
ユーザーが "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 }};
必須のテーブルエイリアス¶
クエリのトップレベルでは、Snowflakeがクエリ内で結合および列のポリシーを正しく検証するために、すべてのテーブルまたはサブクエリのエイリアスを P
(プロバイダーテーブルの場合) または C
(コンシューマーテーブルの場合) として作成する必要があります。結合または列のポリシーに対して検証が必要な列は、 P
または C
でエイリアスされたテーブルに含まれています。(P
や C
を指定することで、プロバイダーとコンシューマーのどちらのポリシーで列を検証するかを、バックエンドが判断できます)。
クエリで複数のプロバイダーまたはコンシューマーテーブルを使用する場合は、初回以降、各テーブルのエイリアスに1から始まるシーケンシャルな接尾辞を追加します。つまり、プロバイダーテーブルの場合は、初回から3回目まで P
、 P1
、 P2
となり、コンシューマーテーブルの場合は、初回から3回目まで C
、 C1
、 C2
となります。 P
または C
のインデックスは、隙間なく連続している必要があります (つまり、 P
、 P2
、 P4
ではなく、 P
、 P1
、 P2
のようにエイリアスを作成します)。
例
SELECT col1 FROM IDENTIFIER({{ source_table[0] }}) AS P;
テンプレートフィルター¶
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 }}
これは、 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);
$$
);
セキュリティに関する考慮事項¶
テンプレートはclean roomのネイティブアプリケーションが実行する単一の SELECT クエリによって評価される必要があります。現在のユーザーのIDでは、テンプレートは実行されません。
ユーザーはclean room内のデータに直接アクセスするのではなく、すべてのアクセスがテンプレート結果を介して、ネイティブアプリケーションで行われます。
テンプレートの中で明示的に列名を定義した場合でも、列やテーブルがプロバイダーによって提供される場合でも、 クエリで列が使用されると必ず、ポリシーフィルターが適用されます。 結合や列のポリシーを後から変更したり、列を変更したりした際に、テンプレートの更新を忘れないでください。ユーザーが入力した列に対し、 join_policy
、 column_policy
、 join_and_column_policy
、 activation_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_name
とenabled_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;
-- analysis_results can be whatever name you want.
BEGIN
CREATE OR REPLACE TABLE cleanroom.activation_data_analysis_results 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 'analysis_results';
END;
次のステップ¶
テンプレートシステムの説明は以上です。以下では、clean roomの実装についてテンプレートタイプ別に説明します:
プロバイダーテンプレート は、プロバイダーが作成するテンプレートです。これがデフォルトのユースケースです。
コンシューマーテンプレート コンシューマーが作成するテンプレートです。場合によっては、clean roomの作成者が、自身のテンプレートをclean roomに作成、アップロード、実行することをコンシューマーに許可したい場合があります。
アクティベーションテンプレート 実行に成功すると結果テーブルが作成されます。アクティベーションテンプレートに応じて、結果テーブルはclean room外のプロバイダーまたはコンシューマーのアカウントに保存されることも、アクティベーションハブにリストされたサードパーティのアクティベーションプロバイダーに送信されることもあります。
連鎖テンプレート 複数のテンプレートを連鎖させて、各テンプレートの出力を次のテンプレートで使用することができます。