맞춤 템플릿을 디자인해요.

맞춤 템플릿을 디자인해요.


기능 — 일반 공급

현재 이들 리전에서 사용할 수 있어요.

정부 및 VPS 배포 환경에서는 사용할 수 없어요.


출처: 문서

본문

clean room 템플릿 정보

clean room 템플릿은 JinjaSQL로 작성해요. JinjaSQL은 Jinja 템플릿 언어의 확장이에요. JinjaSQL 템플릿은 clean room에서 실행하면 SQL 문으로 평가돼요. JinjaSQL 템플릿 언어는 논리 문과 런타임 변수 치환을 제공해서 템플릿을 런타임에 사용자 지정할 수 있게 해줘요. 예를 들어 사용자가 템플릿을 실행할 때 테이블 이름과 열 이름을 제공할 수 있고, 템플릿은 전달된 값에 따라 스스로 조정할 수 있어요.

템플릿에는 일반적으로 두 가지 유형이 있어요:

  • 분석 템플릿은 SQL DQL 문(SELECT 문)으로 평가되어 쿼리 결과를 템플릿 실행자에게 즉시 반환해요.

  • 활성화 템플릿은 결과를 즉시 환경에 표시하는 대신 Snowflake 계정으로 결과를 활성화하는 데 사용돼요. 활성화 템플릿은 몇 가지 추가 요구 사항이 있다는 점을 제외하면 분석 템플릿과 매우 유사하며, DDL 문(CREATE TABLE)으로 평가돼요.

사용자 지정 템플릿 만들기, 공유 및 실행

모든 협업자는 협업 내 특정 분석 실행자와 템플릿을 등록하고 공유할 수 있어요.

간단한 SQL 쿼리와 그것이 템플릿으로 어떻게 작성되는지부터 살펴볼게요.

1. JinjaSQL 템플릿

이메일로 두 테이블을 조인하고 도시별 중복 개수를 보여 주는 간단한 SQL 쿼리예요:

SELECT COUNT(*), city FROM table_1
  INNER JOIN table_2
  ON table_1.hashed_email = table_2.hashed_email
  GROUP BY city;

이 쿼리가 호출자가 JOIN 및 GROUP BY 열과 사용할 테이블을 선택할 수 있게 해 주는 JinjaSQL 템플릿으로 어떻게 보이는지 보여 드릴게요. 이 템플릿에는 Snowflake Data Clean Room 정책을 적용하는 몇 가지 필터가 포함되어 있어요.

SELECT COUNT(*), IDENTIFIER({{ group_by_col | column_policy }})
  FROM IDENTIFIER({{ source_table[0] }}) AS p1
  INNER JOIN IDENTIFIER({{ source_table[1] }}) AS p2
  ON IDENTIFIER({{ p1_join_col | join_policy }}) = IDENTIFIER({{ p2_join_col | join_policy }})
  GROUP BY IDENTIFIER({{ group_by_col | column_policy }});

템플릿에 대한 참고 사항:

  • {{ double bracket pairs }} 안의 값은 변수예요. 값은 호출자가 채워 넣어요.

  • group_by_col, source_table, p1_join_col, p2_join_col은 모두 호출자가 채워 넣는 변수예요. 이 변수들은 템플릿 디자이너가 선택한 임의의 이름을 가지고 있어요.

  • source_table은 Snowflake에서 정의한 표준 변수예요. 이 변수는 쿼리에서 사용할 뷰를 정의해요. 이 뷰들은 clean room에 연결된 데이터 오퍼링 내의 데이터셋이에요. 협업자는 VIEW_DATA_OFFERINGS를 호출하여 사용 가능한 데이터셋을 나열할 수 있어요.

  • Snowflake Data Clean Room 정책을 적용하려면 데이터셋의 별칭을 소문자 p로 지정해야 해요. 템플릿이 여러 데이터셋을 사용하는 경우 첫 번째는 p 또는 p1이고, 추가 데이터셋은 p2, p3 등으로 인덱싱돼요.

  • IDENTIFIER는 모든 열 이름과 테이블 이름에 필요해요. {{ double brackets }} 안의 변수는 문자열 리터럴로 평가되기 때문에 유효한 식별자가 아니거든요.

  • JinjaSQL 필터는 열에 적용되어 해당 열에 Snowflake Data Clean Room 정책을 적용해요. Snowflake는 사용자 지정 필터 join_policy와 column_policy를 구현하는데, 이 필터는 각각 열이 clean room의 조인 정책 또는 열 정책을 준수하는지 확인하고, 준수하지 않으면 쿼리를 실패시켜요. 필터는 {{ *column_name* | *filter_name* }} 형태로 열 이름에 적용돼요.

이 모든 내용은 나중에 자세히 다룰게요.

2. 협업 템플릿

템플릿은 YAML 사양에 포함하고 등록한 다음 연결하여 협업에 추가해요.

CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.REGISTRY.REGISTER_TEMPLATE(
  $$
  api_version: 2.0.0
  spec_type: template
  name: my_test_template
  version: 2026_01_12_V1
  type: sql_analysis
  description: A test template
  methodology: Join on single column with a single group by value
  parameters:
  - name: source_tables
    description: Tables from both sides which can be listed in any order, aliased with p1 or p2
    required: true
  - name: p1_join_col
    description: Column to join on from first table specified under source_tables, aliased with p1
    required: true
  - name: p2_join_col
    description: Column to join on from second table specified under source_tables, aliased with p2
    required: true
  - name: group_by_col
    description: Column which results should be grouped group aliased with respective table p1 or p2
    required: true

  template:
    SELECT COUNT(*), IDENTIFIER({{ group_by_col | column_policy }})
    FROM IDENTIFIER({{ source_table[0] }}) AS p1
    INNER JOIN IDENTIFIER({{ source_table[1] }}) AS p2
    ON IDENTIFIER({{ p1_join_col | join_policy }}) = IDENTIFIER({{ p2_join_col | join_policy }})
    GROUP BY IDENTIFIER({{ group_by_col | column_policy }});

$$);

특정 분석 실행자와 템플릿을 공유하려면 요청해야 하며, 분석 실행자는 요청을 수락하거나 거부할 수 있어요. 또한 템플릿이 공유되려면 해당 분석 실행자의 모든 데이터 제공자가 요청을 수락해야 해요.

-- Request to share template with only Collaborator3.
CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.COLLABORATION.ADD_TEMPLATE_REQUEST(
  $collaboration_name,
  $template_id,
  ['Collaborator3']
);

3. 템플릿 실행

분석 실행자가 이 템플릿을 코드에서 실행하는 방법을 보여 드릴게요. 열 이름이 템플릿에 선언된 테이블 별칭으로 한정되는 방식에 주목해 주세요.

CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.COLLABORATION.RUN( $collaboration_name,
$$
api_version: 2.0.0
spec_type: analysis
name: example_run
description: Example run for template
template: $template_id

template_configuration:
  view_mappings:
    source_tables:
      - collaborator_1.data_offering_1.dataset_1
      - collaborator_2.data_offering_2.dataset_2
  arguments:
     p1_join_col: p1.hashed_email
     p2_join_col: p2.hashed_email
     group_by_col: p2.device_type

$$ );

사용자 지정 템플릿 개발

clean room 템플릿은 JinjaSQL 템플릿이에요. 템플릿을 만들려면 다음 주제에 익숙해야 해요:

Cortex Code를 사용하여 제공해야 하는 변수 입력을 기반으로 JinjaSQL 템플릿의 SQL 출력을 검증할 수 있어요. 아래 예시 프롬프트를 Cortex Code에 복사하여 테스트할 수 있는 최종 SQL 출력을 얻을 수 있어요.

예시:

Resolve the following Jinja template into SQL based on the variables defined:

Jinja Template:
 SELECT IDENTIFIER({{ col1 | column_policy }}), IDENTIFIER({{ col2 | column_policy }})
  FROM IDENTIFIER({{ source_table[0] }}) AS p1
  JOIN IDENTIFIER({{ source_table[1] }}) AS p2
  ON  IDENTIFIER({{ p1_join_col | join_policy }}) = IDENTIFIER({{ p2_join_col | join_policy }})
  {% if where_phrase %} WHERE {{ where_phrase | sqlsafe }}{% endif %};

Variable Inputs:
source_table: SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS, SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS
col1: p1.status
col2: p1.age_band
p1_join_col: p1.hashed_email
p2_join_col: p2.hashed_email
where_phrase: p1.household_size > 2

렌더링된 템플릿은 다음과 같아요:

SELECT IDENTIFIER('p1.status'), IDENTIFIER('p1.age_band')
FROM IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS p1
JOIN IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS p2
ON  IDENTIFIER('p1.hashed_email') = IDENTIFIER('p2.hashed_email')
WHERE p1.household_size > 2;

위 SQL 문을 사용자 환경에서 실행하여 제대로 작동하고 예상 결과를 얻는지 확인해 보세요.

그런 다음 WHERE 절 없이 템플릿을 테스트해 보세요:

Resolve the following Jinja template into SQL based on the variables defined:

Jinja Template:
 SELECT IDENTIFIER({{ col1 | column_policy }}), IDENTIFIER({{ col2 | column_policy }})
  FROM IDENTIFIER({{ source_table[0] }}) AS p1
  JOIN IDENTIFIER({{ source_table[1] }}) AS p2
  ON  IDENTIFIER({{ p1_join_col | join_policy }}) = IDENTIFIER({{ p2_join_col | join_policy }})
  {% if where_phrase %} WHERE {{ where_phrase | sqlsafe }}{% endif %};

Variable Inputs:
source_table: SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS, SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS
col1: p1.status
col2: p1.age_band
p1_join_col: p1.hashed_email
p2_join_col: p2.hashed_email

렌더링된 템플릿:

SELECT IDENTIFIER('p1.status'), IDENTIFIER('p1.age_band')
FROM IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS p1
JOIN IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS p2
ON  IDENTIFIER('p1.hashed_email') = IDENTIFIER('p2.hashed_email');

템플릿을 clean room에 추가하고 분석 실행 사양으로 테스트해 보세요.

데이터 보호

템플릿은 협업자가 clean room에 연결한 데이터셋만 액세스할 수 있어요.

협업자는 데이터셋에 조인, 열, 활성화 정책을 지정하여 해당 열만 템플릿 변수의 입력으로 사용될 수 있도록 해요.

중요

템플릿에는 정책이 적용되도록 열에 적절한 JinjaSQL 정책 필터가 포함되어야 해요.

사용자 지정 템플릿 구문

Snowflake Data Clean Rooms는 몇 가지 확장 기능과 함께 V3 JinjaSQL을 지원해요.

이 섹션에는 다음 주제가 포함되어 있어요:


템플릿 이름 규칙

템플릿을 만들 때 이름에는 문자, 숫자, 밑줄만 포함해야 해요. 템플릿 이름은 템플릿을 등록할 때 템플릿 사양의 name 필드에 지정해요.

유효한 이름 예시:

  • my_template

  • activation_template_1

잘못된 이름 예시:

  • my template - 공백은 허용되지 않아요

  • my_template! - 특수 문자는 허용되지 않아요

템플릿 변수

템플릿 호출자는 템플릿 변수에 값을 전달할 수 있어요. JinjaSQL 구문은 {{ double_brackets }} 안의 모든 변수 이름에 대해 변수 바인딩을 지원하지만, Snowflake는 아래에서 설명하는 것처럼 재정의하지 말아야 할 몇 가지 변수 이름을 예약하고 있어요.

주의

모든 변수는 Snowflake 정의 변수든 사용자 정의 변수든 사용자가 값을 채우므로 적절히 주의해서 다뤄야 해요. 분석 템플릿은 단일 SELECT 문으로 해석되어야 하며(활성화 템플릿은 스크립트 블록으로 해석돼요), 모든 변수는 호출자가 전달한다는 점을 기억하세요.

Snowflake 정의 변수

모든 클린 룸 템플릿은 Snowflake가 정의한 다음 전역 변수에 접근할 수 있어요. 분석 실행기는 source_table과 my_table을 전달하고, 템플릿 작성자는 preset_tables를 정의해요.

source_table:

데이터 오퍼링에서 LINK_DATA_OFFERING을 통해 협업에 연결된 테이블과 뷰의 0부터 시작하는 문자열 배열이에요. 템플릿에서 사용할 수 있어요.

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

my_table:

Collaboration 클린 룸에서 my_table은 Snowflake Standard Edition 사용자만 사용해요. 이 사용자들에게 my_table은 분석 실행기가 LINK_LOCAL_DATA_OFFERING을 호출하여 연결한 데이터셋의 0부터 시작하는 문자열 배열이에요.

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

preset_tables:

분석 실행기가 런타임에 제공하는 대신 템플릿 작성자가 presets in the template한(프리뷰) 데이터셋의 맵이에요. 각 데이터셋은 preset_tables 블록에 선언된 별칭을 키로 해요.

예시: SELECT publisher.col1 FROM IDENTIFIER({{ preset_tables['publisher'] }}) AS publisher;

사용자 정의 변수

템플릿 작성자는 분석 실행기가 값을 채울 수 있는 임의의 변수를 템플릿에 포함할 수 있어요. 이러한 변수는 Snowflake 정의 변수나 테이블 별칭 이름을 제외하고 Jinja 규격을 준수하는 이름을 가질 수 있어요. 필수 변수와 선택 변수에 대한 지침은 템플릿의 매개변수 섹션에서 제공해야 해요.

사용자 정의 변수는 템플릿에서 접근할 수 있어요. 다음은 사용자 정의 변수 max_income에 대한 예시예요.

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

분석 실행기는 analysis run spec에 정의된 대로 RUN을 호출할 때 변수를 전달해요.

변수 올바르게 해석하기

템플릿에 전달된 문자열 값은 최종 템플릿에서 문자열 리터럴로 해석돼요. 바인딩된 변수를 적절히 처리하지 않으면 SQL 구문 오류나 논리 오류가 발생할 수 있어요.

  • SELECT {{ my_col }} FROM p; - 이는 SELECT 'my_col' from p;로 해석되어 문자열 “my_col”을 그대로 반환해요. 아마 원하는 결과가 아닐 거예요.

  • SELECT age FROM {{ source_table[0] }} AS p; - 이는 SELECT age FROM 'somedb.somesch.source_table' AS p;로 해석되어 구문 오류가 발생해요. 테이블은 식별자여야 하지 문자열 리터럴이 아니기 때문이에요.

  • SELECT age FROM IDENTIFIER({{ source_table[0] }}) AS p {{ where_clause }}; - “WHERE age < 50”을 전달하면 SELECT age FROM mytable AS p 'WHERE age < 50';로 계산되어 구문 오류가 발생해요. WHERE 절이 문자열 리터럴이 되기 때문이에요.

따라서 적절한 경우 변수를 반드시 해석해야 해요. 템플릿에서 변수를 올바르게 해석하는 방법은 다음과 같아요.

테이블 및 열 이름 해석

테이블이나 열 이름을 지정하는 변수는 템플릿에서 다음 두 가지 방법 중 하나로 식별자로 변환해야 해요.

  • IDENTIFIER: 예를 들어: SELECT IDENTIFIER({{ my_column }}) FROM p;

  • sqlsafe: 이 JinjaSQL 필터는 식별자 문자열을 SQL 텍스트로 해석해요. 앞의 예와 동일한 문장은 SELECT {{ my_column | sqlsafe }} FROM p;이에요.

IDENTIFIER를 사용할지 sqlsafe를 사용할지는 특정 사용 방식에 따라 달라져요. 예를 들어 p.{{ my_column | sqlsafe }}는 IDENTIFIER로 쉽게 다시 작성할 수 없어요.

동적 SQL 해석

WHERE 절과 같이 리터럴 SQL로 사용해야 하는 문자열 변수가 있다면 템플릿에서 sqlsafe 필터를 사용해요. 예를 들어:

SELECT age FROM IDENTIFIER({{ source_table[0] }}) AS p WHERE {{ where_clause }};

사용자가 where_clause에 “age < 50”을 전달하면 쿼리는 SELECT age FROM *sometable* AS p WHERE 'age < 50';로 해석되어 WHERE 조건이 문자열 리터럴이 되므로 유효하지 않은 SQL이 돼요. 이 경우에는 sqlsafe 필터를 사용해야 해요:

SELECT age FROM IDENTIFIER( {{ source_table[0] }} ) as p {{ where_clause | sqlsafe }};

필수 테이블 별칭

쿼리의 최상위 수준에서 모든 source_table 데이터셋은 p로 별칭을 지정하고, 모든 my_table 데이터셋은 c로 별칭을 지정해야 해요. 그래야 Snowflake가 쿼리에서 조인 및 열 정책을 올바르게 검증할 수 있어요. 조인 또는 열 정책에 대해 검증해야 하는 모든 열은 소문자 p 또는 c 테이블 별칭으로 한정해야 해요.


쿼리에서 여러 source_table 또는 my_table 데이터 세트를 사용하는 경우, 첫 번째 이후의 각 테이블 별칭에 1부터 시작하는 숫자 접미사를 순서대로 추가하세요. 즉, 첫 번째, 두 번째, 세 번째 source_table 데이터 세트에는 p 또는 p1, p2, p3 등을 사용하고, 첫 번째, 두 번째, 세 번째 my_table 데이터 세트에는 c 또는 c1, c2, c3 등을 사용하세요. p 또는 c 인덱스는 중간에 비는 숫자 없이 순서대로 있어야 해요 (즉, p1, p2, p3 별칭을 만들고 p1, p2, p4를 만들지 마세요).

p 및 c 별칭은 예약되어 있으므로, preset table (미리 보기)은 템플릿 본문에서 이 별칭을 사용할 수 없어요. 사전 설정 테이블에는 유효한 Snowflake identifier인 다른 SQL 별칭을 지정하세요.

예시

SELECT p1.col1 FROM IDENTIFIER({{ source_table[0] }}) AS p1
UNION
SELECT p2.col1 FROM IDENTIFIER({{ source_table[1] }}) AS p2;

사용자 지정 clean room 템플릿 필터

Snowflake는 standard Jinja filters와 표준 JinjaSQL filters 대부분을 지원하며, 몇 가지 확장 기능도 함께 제공해요:

join_policy:

열이 데이터 소유자의 조인 정책에 포함되어 있으면 성공하고, 그렇지 않으면 실패해요. Applying data protection policies to data offerings를 참조하세요.

column_policy:

열이 데이터 소유자의 열 정책에 포함되어 있으면 성공하고, 그렇지 않으면 실패해요. Applying data protection policies to data offerings를 참조하세요.

activation_policy:

열이 데이터 소유자의 활성화 정책에 포함되어 있으면 성공하고, 그렇지 않으면 실패해요. Applying data protection policies to data offerings를 참조하세요.

join_and_column_policy:

열이 데이터 소유자의 조인 또는 열 정책에 포함되어 있으면 성공하고, 그렇지 않으면 실패해요. Applying data protection policies to data offerings를 참조하세요.

identifier:

이 JinjaSQL 필터는 Snowflake 템플릿에서 지원되지 않아요.

팁

JinjaSQL 문은 왼쪽에서 오른쪽으로 평가돼요:

  • {{ my_col | column_policy }} 올바른 예

  • {{ my_col | sqlsafe | column_policy }} 올바른 예

  • {{ column_policy | my_col }} 잘못된 예

  • {{ my_col | column_policy | sqlsafe }} 잘못된 예: column_policy는 my_col 값을 문자열로 확인하므로 오류가 발생해요.

clean room 정책 적용

clean room은 템플릿에서 사용되는 열에 대해 clean room 정책을 자동으로 확인하지 않아요. 열에 정책을 적용하려면 다음을 수행하세요:

  • 템플릿의 해당 열에 적절한 policy filter를 적용해야 해요. 예를 들어:
FROM IDENTIFIER({{ source_table[0] }}) AS p1
JOIN IDENTIFIER({{ source_table[1] }}) AS p2
  ON IDENTIFIER({{ p1_join_col | join_policy }}) = IDENTIFIER({{ p2_join_col | join_policy }})

정책은 clean room 내에서 공유되는 뷰를 참조하는 source_table 변수에 포함된 테이블의 열에 대해서만 확인돼요. 정책은 clean room 내에서 공유되지 않는 로컬 테이블인 my_table 변수에 포함된 테이블의 열에 대해서는 확인되지 않아요.

정책을 테스트할 때 열 이름은 모호하지 않아야 해요. 따라서 두 테이블에 같은 이름의 열이 있는 경우, 해당 열에 대해 정책을 테스트하려면 열 이름을 한정해야 해요.

사전 설정 테이블

Preview Feature — 공개

모든 계정에서 사용할 수 있어요.

분석 실행기는 RUN을 호출할 때 source_tables에 전달하여 템플릿이 읽는 모든 데이터 세트를 선택해요. 템플릿 작성자는 대신 템플릿에서 데이터 세트를 사전 설정할 수 있어요. 그러면 템플릿이 항상 해당 데이터 세트를 읽고 분석 실행기가 데이터 세트를 제공하지 않아요. 다음 중 하나를 수행하려면 사전 설정 테이블을 사용하세요:

  • 템플릿이 읽는 데이터 세트를 보장하여 분석 실행기가 다른 데이터 세트로 대체할 수 없도록 해요.

  • RUN 호출을 단순화하여 분석 실행기가 필터 및 차원과 같은 런타임 값만 제공하고 공급자의 데이터 세트 이름을 알 필요가 없도록 해요.

템플릿은 일부 데이터 세트를 사전 설정하고 다른 데이터 세트는 분석 실행기에서 가져올 수 있어요.

사전 설정 테이블 선언 및 참조

각 사전 설정 테이블을 템플릿 사양의 ``preset_tables block에 선언하세요. 각 항목은 사용자가 선택한 alias를 사전 설정할 데이터 세트의 template_view_name에 매핑하며, 형식은 *collaborator_alias*.*data_offering_ID*.*dataset_alias*예요. 데이터 오퍼링이 이미 협업에 연결된 경우, VIEW_DATA_OFFERINGS가 반환하는 TEMPLATE_VIEW_NAME 열에서 값을 복사할 수 있어요. 협업이 존재하기 전이나 후에 데이터 세트를 사전 설정하는 템플릿을 등록할 수 있어요. 사전 설정된 template_view_name은 템플릿이 협업에 추가될 때와 분석이 실행될 때 다시 확인되기 때문이에요.

템플릿 본문에서 사전 설정 테이블을 해당 별칭으로 참조하세요. 이때 첨자(subscript) 표기법이나 점(dot) 표기법을 사용할 수 있어요. 두 형식은 동일해요:



{{ preset_tables['*alias*'] }}

{{ preset_tables.*alias* }}

source_table과 마찬가지로 preset 테이블은 문자열 리터럴로 확인되므로, 테이블 이름으로 사용하려면 IDENTIFIER로 감싸야 해요.

다음 템플릿은 게시자의 오디언스 데이터셋을 preset으로 설정하고, 분석 실행자가 제공하는 테이블에 조인해요.

CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.REGISTRY.REGISTER_TEMPLATE(
  $$
  api_version: 2.0.0
  spec_type: template
  name: preset_overlap_template
  version: 2026_08_18_V1
  type: sql_analysis
  description: Overlap count against a fixed publisher audience
  methodology: Joins the preset publisher audience to a runner-supplied table on hashed email
  parameters:
  - name: source_tables
    description: The advertiser table to compare against the publisher audience
    required: true

  preset_tables:
  - alias: publisher
    template_view_name: pub.pub_audience_v2.AUDIENCE

  template:
    SELECT COUNT(DISTINCT publisher.hashed_email) AS overlap_count
    FROM IDENTIFIER({{ preset_tables['publisher'] }}) AS publisher
    INNER JOIN IDENTIFIER({{ source_table[0] }}) AS p1
    ON publisher.hashed_email = p1.hashed_email;

$$);

분석 실행자는 광고주 테이블만 제공하며, preset으로 설정된 게시자 데이터셋에는 아무것도 제공하지 않아요.

CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.COLLABORATION.RUN( $collaboration_name,
$$
api_version: 2.0.0
spec_type: analysis
template: $template_id

template_configuration:
  view_mappings:
    source_tables:
      - adv.adv_customers_v1.CUSTOMERS

$$ );

템플릿이 읽는 모든 데이터셋을 preset으로 설정하면, 분석 실행자는 분석 스펙에서 source_tables를 완전히 생략해요. RUN의 파라미터 형식에서는 그 대신 template_view_names에 빈 배열을 전달해요.

템플릿은 preset 테이블을 custom function에 인수로 전달할 수도 있어요. source_table 데이터셋을 전달하는 것과 같은 방식이에요. preset 테이블은 source_table이 확인되는 것과 동일한 뷰 이름 문자열로 확인되므로, 직접 전달하면 돼요.

SELECT cleanroom.audience_stats$row_count({{ preset_tables['publisher'] }}) AS publisher_rows;

custom function을 호출하는 모든 템플릿과 마찬가지로, 함수의 코드 스펙을 템플릿의 code_specs 필드에 나열해야 해요.

열 액세스 및 데이터 보호

DCR policy filters는 preset 테이블에 적용되지 않아요. join_policy, column_policy 또는 다른 정책 필터를 preset 테이블의 열에 적용할 수 없어요. 정책은 분석 실행자가 제공하는 데이터셋에 대해서만 평가되기 때문이에요. 정책은 템플릿 작성자가 제어하지 않는 데이터셋을 제한하기 위해 존재하며, preset 테이블은 템플릿 작성자가 선택하는 것이기 때문이에요.

preset 테이블의 열은 쿼리에서 테이블에 지정한 SQL 별칭으로 한정하는 일반 SQL로 참조해요. 해당 열 중 하나에 정책 필터를 적용하면 분석이 제한되는 것이 아니라 PresetTableUnsupportedReferenceError로 실패해요.

-- Supported
SELECT publisher.hashed_email FROM IDENTIFIER({{ preset_tables['publisher'] }}) AS publisher;

-- Not supported
SELECT IDENTIFIER({{ publisher.hashed_email | column_policy }}) FROM IDENTIFIER({{ preset_tables['publisher'] }}) AS publisher;

따라서 분석이 preset 테이블의 어떤 열을 읽을 수 있는지에 대한 유일한 제어 수단은 템플릿 본문이에요. 다음 사항을 염두에 두세요.

  • 열 치환(substitution)에 주의하세요. Snowflake는 정책 필터가 있는 호출자 제공 열은 거부하지만, 필터가 없는 호출자 제공 열은 통과시켜요. 따라서 템플릿이 필터링되지 않은 변수를 preset 테이블에 적용하면, 분석 실행자는 데이터 오퍼링이 노출하는 모든 열을 지정할 수 있어요. 예를 들어 SELECT IDENTIFIER({{ my_col }}) FROM IDENTIFIER({{ preset_tables['publisher'] }}) AS publisher;는 실행자가 게시자 데이터셋의 노출된 모든 열을 읽을 수 있게 해요. 대신 템플릿 본문에서 노출하려는 열을 명시하세요.

  • 호출자가 데이터셋의 열을 선택할 수 있어야 한다면, 해당 데이터셋을 preset으로 설정하는 대신 분석 실행자가 source_tables에 제공하게 하고 적절한 정책 필터를 적용하세요. 템플릿은 여전히 자체 source_table 데이터셋의 열에 정책 필터를 적용할 수 있어요.

제한 사항

템플릿 본문에서 preset 테이블에 유효한 Snowflake identifier인 SQL 별칭을 지정하세요. 단, p, c, 또는 p나 c 뒤에 숫자가 오는 별칭은 제외해요. 그 별칭들은 source_table 및 my_table 데이터셋을 위해 예약되어 있어요 (Those aliases are reserved). 템플릿 등록은 preset 테이블이 예약된 SQL 별칭을 사용하면 실패해요.

데이터 제공자는 자신의 데이터셋을 preset으로 설정하는 템플릿에 대한 승인 권한을 유지해요. 모든 템플릿과 마찬가지로, 분석 실행자의 모든 데이터 제공자는 해당 실행자와 request to share the template를 승인해야 해요.

액세스 고려 사항 및 모범 사례

템플릿은 항상 clean room 애플리케이션 역할의 컨텍스트에서 실행돼요. 협업자는 템플릿 액세스 전용으로 제한된 clean room 내 데이터에 직접 액세스할 수 없어요. 모든 액세스는 네이티브 애플리케이션 역할과 템플릿 출력을 통해 이루어져요.

모범 사례로, clean room에서 만들거나 사용하는 템플릿에 대해 다음을 따르는 것이 좋아요.

  • 템플릿에서 열 변수를 사용할 때마다 정책 필터가 적용되도록 하여 협업자 정책이 존중되게 하세요.

  • 가능하면 사용자 제공 변수를 IDENTIFIER()로 감싸서 SQL 인젝션 공격에 대한 템플릿을 강화하세요.

활성화 템플릿

템플릿은 쿼리 결과를 clean room 외부의 테이블에 저장하는 데에도 사용할 수 있어요. 이를 *활성화(activation)*라고 해요. 활성화 템플릿은 다음과 같은 추가 요구 사항이 있는 분석 템플릿이에요.

  • 활성화 템플릿은 SQL 스크립트 블록으로 평가되는 JinjaSQL 문이에요. 단순 SELECT 문일 수 있는 분석 템플릿과는 달라요.

  • 활성화 템플릿은 결과를 저장하기 위해 clean room 내부에 내부 테이블을 만들어야 해요. 템플릿이 생성하는 테이블은 cleanroom.activation_data_ 접두사를 가져야 해요. 예: cleanroom.activation_data_my_results

  • 내부 결과 테이블의 모든 열은 데이터 오퍼링 사양에 activation_allowed: TRUE 값을 가져야 해요.

  • 스크립트 블록은 생성된 테이블의 이름을 cleanroom.activation_data_ 접두사 없이 반환하는 RETURN 문으로 끝나야 해요. 예: RETURN 'my_results'

  • 템플릿 자체에는 명명 요구 사항이 없어요.

다음은 활성화 템플릿 사양의 예시예요.

api_version: 2.0.0
spec_type: template
name: my_activation_template
version: v0
type: sql_activation
description: Activation template that creates segment data
parameters:
  - name: p1_join_column
    description: Join policy column in the first (provider) table, such as a hashed email column
    required: true
  - name: p2_join_column
    description: Join policy column in the second (consumer) table, such as a hashed email column
    required: true
  - name: activation_column
    description: Activation column in the first table (customer ID)
    required: true
template: |
  BEGIN
      CREATE OR REPLACE TABLE cleanroom.activation_data_analysis_results AS
      SELECT
          p1.{{ activation_column | sqlsafe | activation_policy }} AS customer_id
      FROM IDENTIFIER({{ source_table[0] }}) AS p1
      JOIN IDENTIFIER({{ source_table[1] }}) AS p2
          ON p1.{{ p1_join_column | sqlsafe | join_policy }} = p2.{{ p2_join_column | sqlsafe | join_policy }};
      RETURN 'analysis_results';
  END;

활성화 동시 실행

활성화 템플릿의 각 실행은 결과 테이블에 대해 CREATE OR REPLACE TABLE을 실행해요. 앞선 예시는 고정된 테이블 이름(cleanroom.activation_data_analysis_results)에 쓰고 있어요. 해당 템플릿의 여러 실행이 동시에 수행되면 충돌이 발생해요. 한 실행이 테이블을 교체하거나 삭제하는 동안 다른 실행이 여전히 그 테이블을 사용 중일 수 있기 때문에 일부 실행이 실패할 수 있어요. (이로 인해 발생하는 오류는 Troubleshooting을 참조하세요.)

동일한 clean room에서 활성화를 동시에 실행하려면 각 실행에 고유한 결과 테이블 이름을 지정하세요. 템플릿 내부에서 UUID를 생성하고, 이를 cleanroom.activation_data_ 접두사에 추가한 다음, 해당 고유 접미사를 반환하여 실행자가 결과 테이블을 찾을 수 있게 해요. 다음 템플릿은 각 실행에 대해 충돌 없는 테이블 이름을 만든다는 점을 제외하면 이전 예시와 동일해요:

api_version: 2.0.0
spec_type: template
name: my_concurrent_activation_template
version: v0
type: sql_activation
description: Activation template that writes to a unique results table for each run
parameters:
  - name: p1_join_column
    description: Join policy column in the first (provider) table, such as a hashed email column
    required: true
  - name: p2_join_column
    description: Join policy column in the second (consumer) table, such as a hashed email column
    required: true
  - name: activation_column
    description: Activation column in the first table (customer ID)
    required: true
template: |
  DECLARE
      activation_uuid STRING;
      table_name_suffix STRING;
      table_name STRING;
  BEGIN
      SELECT REGEXP_REPLACE(UUID_STRING(), '[^a-zA-Z0-9]', '') INTO :activation_uuid;
      SELECT ('analysis_results_' || :activation_uuid) INTO :table_name_suffix;
      SELECT ('cleanroom.activation_data_' || :table_name_suffix) INTO :table_name;

      CREATE OR REPLACE TABLE IDENTIFIER(:table_name) AS
      SELECT
          p1.{{ activation_column | sqlsafe | activation_policy }} AS customer_id
      FROM IDENTIFIER({{ source_table[0] }}) AS p1
      JOIN IDENTIFIER({{ source_table[1] }}) AS p2
          ON p1.{{ p1_join_column | sqlsafe | join_policy }} = p2.{{ p2_join_column | sqlsafe | join_policy }};

      RETURN :table_name_suffix;
  END;

활성화를 동시에 실행할 필요가 없다면 고정된 테이블 이름을 사용하고 순차적으로 실행할 수 있어요.

협업에서 활성화를 구현하는 방법을 알아보세요: Activating query results.

다음 단계

템플릿 시스템을 익혔다면, 템플릿 유형에 맞는 clean room 구현에 대한 세부 사항을 읽어보세요:

  • Activation templates는 성공적인 실행 후 결과 테이블을 생성하며, 이 테이블은 clean room 외부로 공유돼요. 협업 사양에 따라 결과 테이블은 분석 실행자나 다른 협업자에게 공유될 수 있어요.

  • Code specs는 사용자 정의 Python UDF 및 UDTF를 협업에 업로드하는 데 사용돼요. 협업 내 템플릿은 이러한 함수를 실행하여 복잡한 데이터 작업을 수행할 수 있어요.

  • Internal tables는 중간 또는 영구 결과를 저장하는 데 사용되며, 다단계 워크플로우를 지원하기 위해 다운스트림에서 사용될 수 있어요. 이러한 테이블은 clean room 내부의 템플릿이나 사용자 정의 업로드 코드에서 접근할 수 있어요.

추가 정보

더 알아보기 (Learn more)