EXECUTE IMMEDIATE FROM
EXECUTE IMMEDIATE FROM
스테이지에 있는 파일에 지정된 SQL 문장을 실행하는 명령이에요. 파일에는 SQL 문장 또는 Snowflake Scripting 블록이 포함될 수 있어요. 문장은 구문적으로 올바른 SQL이어야 해요. 모든 Snowflake 세션에서 파일의 문장을 실행할 수 있어요.
출처: 문서
본문
이 기능은 Snowflake 객체와 코드의 배포 및 관리 제어 메커니즘을 제공해요. 예를 들어 모든 계정에 표준 Snowflake 환경을 만들기 위해 저장된 스크립트를 실행할 수 있어요. 구성 스크립트는 각 새 계정에 대해 사용자, 역할, 데이터베이스, 스키마를 만드는 문장을 포함할 수 있어요.
Jinja2 템플릿 (Jinja2 templating)
EXECUTE IMMEDIATE FROM은 Jinja2 템플릿 언어를 사용하는 템플릿 파일도 실행할 수 있어요. 템플릿은 변수와 표현식을 포함할 수 있어서 루프, 조건문, 변수 대체, 매크로 등을 사용할 수 있어요. 템플릿은 다른 템플릿을 포함할 수도 있으며 스테이지의 다른 파일에 정의된 매크로를 가져올 수도 있어요.
실행할 템플릿 파일은 다음이어야 해요:
- 구문적으로 유효한 Jinja2 템플릿.
- 스테이지 또는 Git 리포지토리 클론에 위치.
- 구문적으로 유효한 SQL 문장을 렌더링할 수 있어야 함.
템플릿은 환경 변수를 사용하여 더 유연한 제어 구조와 파라미터화를 가능하게 해요. 템플릿으로 SQL 스크립트를 렌더링하려면 템플릿 지시어(templating directive)를 사용하거나 최소한 하나의 템플릿 변수가 있는 USING 절을 추가해요.
템플릿 지시어 (Templating directive)
두 템플릿 지시어 중 하나를 사용할 수 있어요. 권장 지시어는 유효한 SQL 구문을 사용해요:
--!jinja
선택적으로 대체 지시어를 사용할 수 있어요:
#!jinja
참고: 지시어 앞에는 바이트 순서 표시(BOM)와 최대 10개의 공백 문자(줄바꿈, 탭, 공백)만 올 수 있어요. 같은 줄의 지시어 뒤에 오는 문자는 무시돼요.
템플릿에서 스테이징된 파일의 콘텐츠 사용
템플릿은 SnowflakeFile API를 통해 직접 또는 Jinja2의 include, import, inheritance 기능을 통해 다른 스테이징된 파일을 로드할 수 있어요.
파일은 절대 경로로 참조할 수 있어요:
{% include "@my_stage/path/to/my_template" %}
{% import "@my_stage/path/to/my_template" as my_template %}
{% extends "@my_stage/path/to/my_template" %}
{{ SnowflakeFile.open("@my_stage/path/to/my_template", 'r', require_scoped_url = False).read() }}
include, import, extends는 상대 경로도 지원하며, SnowflakeFile API는 스코프된(scope) Snowflake 파일 URL을 지원해요:
{% include "my_template" %}
{% import "../my_template" as my_template %}
{% extends "/path/to/my_template" %}
구문 (Syntax)
EXECUTE IMMEDIATE
FROM { absoluteFilePath | relativeFilePath }
[ USING ( <key> => <value> [ , <key> => <value> [ , ... ] ] ) ]
[ DRY_RUN = { TRUE | FALSE } ]
여기서:
absoluteFilePath ::=
@[ <namespace>. ]<stage_name>/<path>/<filename>
relativeFilePath ::=
'[ <path> | ./ <path> | ../ <path> ]/ <filename>'
필수 파라미터 (Required parameters)
절대 파일 경로 (absoluteFilePath)
- namespace — 내부 또는 외부 스테이지가 있는 데이터베이스 및/또는 스키마로,
database_name.schema_name또는schema_name형태예요. 사용자 세션에서 데이터베이스와 스키마가 현재 사용 중이면 네임스페이스는 선택 사항이고, 그렇지 않으면 필수예요. - stage_name — 내부 또는 외부 스테이지의 이름.
- path — 스테이지의 파일로 가는 대소문자 구분 경로.
- filename — 실행할 파일의 이름. 구문적으로 올바른 유효한 SQL 문장을 포함해야 해요. 각 문장은 세미콜론으로 구분해야 해요.
상대 파일 경로 (relativeFilePath)
- path — 스테이지의 파일로 가는 대소문자 구분 상대 경로. 상대 경로는 스테이지의 파일 시스템 루트를 나타내는 선행
/, 현재 디렉토리(부모 파일이 있는 디렉토리)를 나타내는./, 부모 디렉토리를 나타내는../같은 기존 규칙을 지원해요. - filename — 실행할 파일의 이름. 구문적으로 올바른 유효한 SQL 문장을 포함해야 해요. 각 문장은 세미콜론으로 구분해야 해요.
선택 파라미터 (Optional parameters)
- USING (
=> — 템플릿 확장을 파라미터화하는 데 사용할 수 있는 하나 이상의 키-값 쌍을 전달할 수 있어요. 키-값 쌍은 쉼표로 구분된 목록을 형성해야 해요. USING 절이 있으면 파일은 SQL 스크립트로 실행되기 전에 먼저 Jinja2 템플릿으로 렌더링돼요. 여기서 key는 템플릿 변수의 이름이고, value는 템플릿에서 변수에 할당할 값이에요. 문자열 값은[ , => [ , ... ] ] ) '또는$$로 묶어야 해요. - DRY_RUN = { TRUE | FALSE } — 파일을 SQL 스크립트로 실행하지 않고 렌더링된 내용만 미리 볼지 여부를 지정해요.
TRUE는 SQL 문장을 실행하지 않고 렌더링된 파일 내용을 반환해요.FALSE는 템플릿에서 SQL 문장을 렌더링하고 그 문장들을 실행해요. 기본값: FALSE
반환값 (Returns)
EXECUTE IMMEDIATE FROM은 다음을 반환해요:
- 모든 문장이 성공적으로 실행되면 파일의 마지막 문장의 결과.
- 파일의 문장 중 하나라도 실패하면 오류 메시지. 파일의 문장에 오류가 있으면
EXECUTE IMMEDIATE FROM명령은 실패하고 실패한 문장의 오류 메시지를 반환해요.
참고:
EXECUTE IMMEDIATE FROM명령이 실패하고 오류 메시지를 반환하면, 실패한 문장 이전의 문장들은 성공적으로 완료된 상태예요.
접근 제어 요구사항 (Access control requirements)
EXECUTE IMMEDIATE FROM명령을 실행하는 역할은 파일이 있는 스테이지에 대해 USAGE(외부 스테이지) 또는 READ(내부 스테이지) 권한이 있어야 해요.- 파일을 실행하는 역할은 권한이 있는 파일의 문장만 실행할 수 있어요. 예를 들어 파일에 CREATE TABLE 문장이 있으면 역할은 계정에서 테이블을 만들 수 있는 권한이 있어야 하며, 그렇지 않으면 문장이 실패해요.
스키마에서 객체를 운영하려면 부모 데이터베이스에 대한 권한이 하나 이상, 부모 스키마에 대한 권한이 하나 이상 필요해요.
사용 메모 (Usage notes)
- 실행할 파일의 SQL 문장은
EXECUTE IMMEDIATE FROM문장을 포함할 수 있어요.- 중첩된
EXECUTE IMMEDIATE FROM문장은 상대 파일 경로를 사용할 수 있어요. 상대 경로는 부모 파일의 스테이지와 파일 경로를 기준으로 평가돼요. 상대 파일 경로가/로 시작하면 경로는 부모 파일을 포함하는 스테이지의 루트 디렉토리에서 시작해요.
- 중첩된
- 상대 파일 경로는 작은따옴표(') 또는
$$로 묶어야 해요. - 중첩 파일의 최대 실행 깊이는 5예요.
- 절대 파일 경로는 선택적으로 작은따옴표(') 또는
$$로 묶을 수 있어요. - 실행할 파일은 10MB보다 클 수 없어요.
- 실행할 파일은 UTF-8로 인코딩되어야 해요.
- 실행할 파일은 압축되지 않아야 해요. PUT 명령으로 내부 스테이지에 파일을 업로드할 때는 명시적으로
AUTO_COMPRESS파라미터를FALSE로 설정해야 해요. 예를 들어my_file.sql을my_stage에 업로드해요:
PUT file://~/sql/scripts/my_file.sql @my_stage/scripts/
AUTO_COMPRESS=FALSE;
- 디렉토리의 모든 파일 실행은 지원되지 않아요. 예를 들어
EXECUTE IMMEDIATE FROM @stage_name/scripts/는 오류를 발생시켜요.
템플릿 사용 메모 (Templating usage notes)
- 템플릿의 변수 이름은 대소문자를 구분해요.
- 템플릿 변수 이름은 선택적으로 큰따옴표로 묶을 수 있어요. 예약 키워드를 변수 이름으로 사용하면 큰따옴표로 묶는 것이 유용해요.
- USING 절에서 다음 파라미터 유형이 지원돼요:
- 문자열.
'또는$$로 묶어야 해요. 예:USING (a => 'a', b => $$b$$). - 숫자(십진수 및 정수). 예:
USING (a => 1, b => -1.23). - 부울. 예:
USING (a => TRUE, b => FALSE). - NULL. 예:
USING (a => NULL).
참고: Jinja2 템플릿 엔진은 NULL 값을 Python NoneType 유형으로 해석해요.
- 세션 변수. 예:
USING (a => $var). 지원되는 데이터 유형의 값을 보유하는 세션 변수만 허용돼요. - 바인드 변수. 예:
USING (a => :var). 지원되는 데이터 유형의 값을 보유하는 바인드 변수만 허용돼요. 저장 프로시저 인자를 템플릿에 전달하는 데 사용할 수 있어요.
- 문자열.
- Snowflake Git 리포지토리나 Snowflake Native App의 파일은 템플릿에서 접근할 수 없어요.
- 템플릿 렌더링의 최대 결과 크기는 100,000바이트예요.
- 템플릿은 Jinja2 버전 3.1.6 템플릿 엔진으로 렌더링돼요.
EXECUTE IMMEDIATE FROM 오류 문제 해결 (Troubleshooting)
다음은 EXECUTE IMMEDIATE FROM 문장에서 발생하는 몇 가지 일반적인 오류와 해결 방법이에요.
파일 오류 (File errors)
| Error | Cause | Solution |
|---|---|---|
001501 (02000): File '<filename>' not found in stage '<stage name>'. |
파일이 존재하지 않거나, 파일 이름이 디렉토리의 루트입니다(예: @stage_name/scripts/). |
파일 이름을 확인하고 파일이 존재하는지 확인하세요. 디렉토리 내의 모든 파일 실행은 지원되지 않습니다. |
001503 (42601): Relative file references like '<path>' cannot be used in top-level EXECUTE IMMEDIATE calls. |
파일 실행 외부에서 상대 파일 경로로 문장이 실행되었습니다. | 상대 파일 경로는 파일 안의 EXECUTE IMMEDIATE FROM 문장에서만 사용할 수 있습니다. 파일에 절대 파일 경로를 사용하세요. |
001003 (42000): SQL compilation error: syntax error line <n> at position <n> unexpected '<token>'. |
파일에 SQL 구문 오류가 있습니다. | 파일의 구문 오류를 수정하고 스테이지에 파일을 다시 업로드하세요. |
스테이지 오류 (Stage errors)
| Error | Cause | Solution |
|---|---|---|
002003 (02000): SQL compilation error: Stage '<stage name>' does not exist or not authorized. |
스테이지가 존재하지 않거나 스테이지에 대한 접근 권한이 없습니다. | 스테이지 이름을 확인하고 스테이지가 존재하는지 확인하세요. 스테이지에 접근하는 데 필요한 권한이 있는 역할로 문장을 실행하세요. |
접근 제어 오류 (Access control errors)
| Error | Cause | Solution |
|---|---|---|
003001 (42501): Uncaught exception of type 'STATEMENT_ERROR' in file <file> on line <n> at position <n>: SQL access control error: Insufficient privileges to operate on schema '<schema>' |
문장을 실행하는 역할이 파일의 일부 또는 전체 문장을 실행하는 데 필요한 권한이 없습니다. | 파일의 문장을 실행할 적절한 권한이 있는 역할을 사용하세요. |
템플릿 오류 (Templating errors)
| Error | Cause | Solution |
|---|---|---|
001003 (42000): SQL compilation error: syntax error line [n] at position [m] unexpected '{'. |
파일에 템플릿 구조(예: {{ table_name }})가 있지만 템플릿 엔진으로 렌더링되지 않았습니다. 템플릿이 렌더링되지 않으면 파일의 텍스트 줄이 SQL 문장으로 실행됩니다. 파일의 템플릿 구조는 SQL 구문 오류를 유발할 가능성이 높습니다. |
템플릿 지시어를 추가하거나 USING 절로 문장을 다시 실행하여 최소 하나의 템플릿 변수를 지정하세요. |
000005 (XX000): Python Interpreter Error: jinja2.exceptions.UndefinedError: '<var>' is undefined in template processing |
템플릿에 사용된 변수 중 일부가 USING 절에 지정되지 않았습니다. | 템플릿의 변수 이름과 개수를 확인하고 USING 절이 모든 템플릿 변수에 대한 값을 포함하도록 업데이트하세요. |
001510 (42601): Unable to use value of template variable '<key>' |
변수 키의 값이 지원되지 않는 유형입니다. | 템플릿 변수 값에 지원되는 파라미터 유형을 사용하고 있는지 확인하세요. |
001518 (42601): Size of expanded template exceeds limit of 100,000 bytes. |
렌더링된 템플릿의 크기가 현재 한도를 초과합니다. | 템플릿 파일을 더 작은 여러 템플릿으로 나누고, 중첩 스크립트에 템플릿 변수를 전달하면서 순차 실행하는 새 스크립트를 추가하세요. |
예시 (Examples)
기본 예시
my_stage 스테이지에 있는 create-inventory.sql 파일을 실행해요.
- 다음 문장으로
create-inventory.sql파일을 만들어요:
CREATE OR REPLACE TABLE my_inventory(
sku VARCHAR,
price NUMBER
);
EXECUTE IMMEDIATE FROM './insert-inventory.sql';
SELECT sku, price
FROM my_inventory
ORDER BY price DESC;
- 다음 문장으로
insert-inventory.sql파일을 만들어요:
INSERT INTO my_inventory
VALUES ('XYZ12345', 10.00),
('XYZ81974', 50.00),
('XYZ34985', 30.00),
('XYZ15324', 15.00);
- 내부 스테이지
my_stage를 만들어요:
CREATE STAGE my_stage;
- PUT 명령으로 두 로컬 파일을 스테이지에 업로드해요:
PUT file://~/sql/scripts/create-inventory.sql @my_stage/scripts/
AUTO_COMPRESS=FALSE;
PUT file://~/sql/scripts/insert-inventory.sql @my_stage/scripts/
AUTO_COMPRESS=FALSE;
my_stage에 있는create-inventory.sql스크립트를 실행해요:
EXECUTE IMMEDIATE FROM @my_stage/scripts/create-inventory.sql;
반환값:
+----------+-------+
| SKU | PRICE |
|----------+-------|
| XYZ81974 | 50 |
| XYZ34985 | 30 |
| XYZ15324 | 15 |
| XYZ12345 | 10 |
+----------+-------+
간단한 템플릿 예시
- 두 변수와 템플릿 지시어가 있는
setup.sql템플릿 파일을 만들어요:
--!jinja
CREATE SCHEMA {{env}};
CREATE TABLE RAW (COL OBJECT)
DATA_RETENTION_TIME_IN_DAYS = {{retention_time}};
- 스테이지를 만들어요 — 파일을 업로드할 수 있는 스테이지가 이미 있으면 선택 사항이에요. 예를 들어 Snowflake에서 내부 스테이지를 만들어요:
CREATE STAGE my_stage;
- 파일을 스테이지에 업로드해요. 예를 들어 로컬 환경에서 PUT 명령으로
setup.sql을my_stage스테이지에 업로드해요:
PUT file://path/to/setup.sql @my_stage/scripts/
AUTO_COMPRESS=FALSE;
setup.sql파일을 실행해요:
EXECUTE IMMEDIATE FROM @my_stage/scripts/setup.sql
USING (env=>'dev', retention_time=>0);
매크로, 조건문, 루프, imports가 있는 템플릿 예시
- 매크로 정의가 포함된 템플릿 파일을 만들어요. 예를 들어 로컬 환경에서
macros.jinja파일을 만들어요:
{%- macro get_environments(deployment_type) -%}
{%- if deployment_type == 'prod' -%}
{{ "prod1,prod2" }}
{%- else -%}
{{ "dev,qa,staging" }}
{%- endif -%}
{%- endmacro -%}
- 템플릿 파일을 만들고 파일 상단에 템플릿 지시어(
--!jinja2)를 추가해요. 템플릿 지시어 뒤에 이전 단계에서 만든 파일에 정의된 매크로를 가져오는 import 문장을 추가해요. 예를 들어 로컬 환경에서setup-env.sql파일을 만들어요:
--!jinja2
{% from "macros.jinja" import get_environments %}
{%- set environments = get_environments(DEPLOYMENT_TYPE).split(",") -%}
{%- for environment in environments -%}
CREATE DATABASE {{ environment }}_db;
USE DATABASE {{ environment }}_db;
CREATE TABLE {{ environment }}_orders (
id NUMBER,
item VARCHAR,
quantity NUMBER);
CREATE TABLE {{ environment }}_customers (
id NUMBER,
name VARCHAR);
{% endfor %}
- 스테이지를 만들어요 — 파일을 업로드할 수 있는 스테이지가 이미 있으면 선택 사항이에요. 예를 들어 Snowflake에서 내부 스테이지를 만들어요:
CREATE STAGE my_stage;
- 파일을 스테이지에 업로드해요. 예를 들어 로컬 환경에서 PUT 명령으로
setup-env.sql과macros.jinja를my_stage스테이지에 업로드해요:
PUT file://path/to/setup-env.sql @my_stage/scripts/
AUTO_COMPRESS=FALSE;
PUT file://path/to/macros.jinja @my_stage/scripts/
AUTO_COMPRESS=FALSE;
- Jinja2 코드에 문제가 없는지 템플릿이 렌더링한 SQL 문장을 미리 봐요:
EXECUTE IMMEDIATE FROM @my_stage/scripts/setup-env.sql
USING (DEPLOYMENT_TYPE => 'prod') DRY_RUN = TRUE;
반환값:
+----------------------------------+
| rendered file contents |
|----------------------------------|
| --!jinja2 |
| CREATE DATABASE prod1_db; |
| USE DATABASE prod1_db; |
| CREATE TABLE prod1_orders ( |
| id NUMBER, |
| item VARCHAR, |
| quantity NUMBER); |
| CREATE TABLE prod1_customers ( |
| id NUMBER, |
| name VARCHAR); |
| CREATE DATABASE prod2_db; |
| USE DATABASE prod2_db; |
| CREATE TABLE prod2_orders ( |
| id NUMBER, |
| item VARCHAR, |
| quantity NUMBER); |
| CREATE TABLE prod2_customers ( |
| id NUMBER, |
| name VARCHAR); |
| |
+----------------------------------+
setup-env.sql파일을 실행해요:
EXECUTE IMMEDIATE FROM @my_stage/scripts/setup-env.sql
USING (DEPLOYMENT_TYPE => 'prod');