CREATE VIEW

하나 이상의 기존 테이블의 쿼리(또는 다른 유효한 쿼리 식)를 기반으로 현재/지정된 스키마에 새 뷰를 만듭니다.

참고 항목:

ALTER VIEW , DROP VIEW , SHOW VIEWS , DESCRIBE VIEW

구문

CREATE [ OR REPLACE ] [ SECURE ] [ { [ { LOCAL | GLOBAL } ] TEMP | TEMPORARY | VOLATILE } ] [ RECURSIVE ] VIEW [ IF NOT EXISTS ] <name>
  [ ( <column_list> ) ]
  [ <col1> [ WITH ] MASKING POLICY <policy_name> [ USING ( <col1> , <cond_col1> , ... ) ]
           [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] ) ]
  [ , <col2> [ ... ] ]
  [ [ WITH ] ROW ACCESS POLICY <policy_name> ON ( <col_name> [ , <col_name> ... ] ) ]
  [ [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] ) ]
  [ COPY GRANTS ]
  [ COMMENT = '<string_literal>' ]
  AS <select_statement>
Copy

필수 매개 변수

name

뷰에 대한 식별자를 지정하는데, 뷰가 생성된 스키마에 대해 고유한 식별자여야 합니다.

또한, 식별자는 알파벳 문자로 시작해야 하며 전체 식별자 문자열을 큰따옴표(예: "My object")로 묶지 않는 한 공백이나 특수 문자를 포함할 수 없습니다. 큰따옴표로 묶인 식별자도 대/소문자를 구분합니다.

자세한 내용은 식별자 요구 사항 섹션을 참조하십시오.

select_statement

뷰를 만드는 데 사용되는 쿼리를 지정합니다. 하나 이상의 원본 테이블 또는 다른 유효한 SELECT 문에 있을 수 있습니다. 이 쿼리는 뷰에 대한 텍스트/정의의 역할을 하며 SHOW VIEWS 출력과 VIEWS Information Schema 뷰에 표시됩니다.

선택적 매개 변수

SECURE

뷰가 안전한 것으로 지정합니다. 보안 뷰에 대한 자세한 내용은 보안 뷰 관련 작업하기 을 참조하십시오.

기본값: 값 없음(뷰가 안전하지 않음)

{ [ { LOCAL | GLOBAL } ] TEMP | TEMPORARY | VOLATILE }

뷰를 생성한 세션 의 지속 시간 동안만 뷰가 유지되도록 지정합니다. 세션이 끝나면 임시 뷰와 모든 내용이 삭제됩니다.

다른 데이터베이스와의 호환성을 위해 TEMPORARY (예: GLOBAL TEMPORARY)의 동의어와 약어가 제공됩니다(예: CREATE VIEW 문을 마이그레이션할 때 오류 방지). 이러한 키워드로 만든 뷰는 TEMPORARY 키워드로 만든 뷰와 똑같이 나타나고 동작합니다.

기본값: 값 없음. TEMPORARY 로 선언되지 않은 뷰는 영구적입니다.

예기치 않은 충돌을 방지하려면 스키마에 이미 있는 뷰의 이름을 따라 임시 뷰의 이름을 지정하지 마십시오.

스키마의 다른 뷰와 이름이 같은 임시 뷰를 만든 경우 뷰에 사용된 모든 쿼리와 작업은 임시 뷰를 삭제할 때까지 세션의 임시 뷰에만 영향을 미칩니다. 뷰를 삭제하면 스키마에 이미 존재하는 뷰가 아닌 임시 뷰가 삭제됩니다.

RECURSIVE

뷰가 꼭 CTE(공용 테이블 식)를 사용할 필요는 없이 재귀 구문을 사용해 자신을 가리킬 수 있음을 지정합니다. 일반적으로는 재귀 뷰, 그리고 특히 RECURSIVE 키워드에 대한 자세한 내용은 재귀 뷰(구체화되지 않은 뷰만 해당) 및 아래의 재귀 뷰 예를 참조하십시오.

기본값: 값 없음(뷰가 재귀적이지 않거나 CTE를 사용해야만 재귀적임)

column_list

열 이름을 변경하거나 새 뷰의 열에 설명을 추가하려면 열 이름을 지정하는 열 목록과 (필요한 경우) 열에 대한 설명을 포함하십시오. (열의 데이터 타입을 지정할 필요가 없습니다.)

뷰의 열이 (단순한 열 이름이 아니라) 식을 기반으로 하는 경우, 뷰의 각 열에 대한 열 이름을 입력해야 합니다. 예를 들어 다음과 같은 경우에는 열 이름이 필요합니다.

CREATE VIEW v1 (pre_tax_profit, taxes, after_tax_profit) AS
    SELECT revenue - cost, (revenue - cost) * tax_rate, (revenue - cost) * (1.0 - tax_rate)
    FROM table1;
Copy

각 열에 대해 선택적 설명을 지정할 수 있습니다. 예:

CREATE VIEW v1 (pre_tax_profit COMMENT 'revenue minus cost',
                taxes COMMENT 'assumes taxes are a fixed percentage of profit',
                after_tax_profit)
    AS
    SELECT revenue - cost, (revenue - cost) * tax_rate, (revenue - cost) * (1.0 - tax_rate)
    FROM table1;
Copy

설명은 열 이름이 아리송할 때 특히 유용합니다.

설명을 보려면 DESCRIBE VIEW 를 사용하십시오.

MASKING POLICY = policy_name

열에 설정할 마스킹 정책 을 지정합니다.

USING ( col_name , cond_col_1 ... )

조건부 마스킹 정책 SQL 식에 전달할 인자를 지정합니다.

목록의 첫 번째 열은 데이터를 마스킹하거나 토큰화할 정책 조건에 대한 열을 지정하며, 마스킹 정책이 설정된 열과 반드시 일치해야 합니다.

추가 열은 첫 번째 열에 대해 쿼리가 수행될 때 쿼리 결과의 각 행에 있는 데이터를 마스킹할지 또는 토큰화할지 결정하기 위해 평가할 열을 지정합니다.

USING 절을 생략하면 Snowflake는 조건부 마스킹 정책을 일반 마스킹 정책 으로 취급합니다.

COPY GRANTS

OR REPLACE 절을 사용하여 새 뷰를 만들 때 원본 뷰에서 액세스 허가를 유지합니다.

이 매개 변수는 OWNERSHIP을 제외한 모든 권한을 기존 뷰에서 새 뷰로 복사합니다. 새 뷰는 스키마의 오브젝트 유형에 대해 정의된 향후 모든 권한 부여를 상속하지는 않습니다. 기본적으로, CREATE VIEW 문을 실행하는 역할은 새 뷰를 소유합니다.

CREATE VIEW 문에 이 매개 변수가 포함되지 않은 경우에는 새 뷰가 원본 뷰에 부여된 모든 명시적인 액세스 권한을 상속하지 않지만, 스키마의 오브젝트 유형에 대해 정의된 향후 모든 권한 부여를 상속하지는 않습니다.

권한 부여 복사 작업은 CREATE VIEW 문에서 원자적으로(즉, 같은 트랜잭션 내에서) 발생합니다.

기본값: 값 없음(권한 부여는 복사되지 않음)

COMMENT = 'string_literal'

뷰에 대한 설명을 지정합니다.

기본값: 값 없음

ROW ACCESS POLICY policy_name ON ( col_name [ , col_name ... ] )

뷰에 설정할 행 액세스 정책 을 지정합니다.

TAG ( tag_name = 'tag_value' [ , tag_name = 'tag_value' , ... ] )

태그 이름과 태그 문자열 값을 지정합니다.

태그 값은 항상 문자열이며, 태그 값의 최대 문자 수는 256자입니다.

문에서 태그를 지정하는 방법에 대한 자세한 내용은 오브젝트 및 열에 대한 태그 할당량 섹션을 참조하십시오.

액세스 제어 요구 사항

이 SQL 명령을 실행하는 데 사용되는 역할 에는 최소한 다음 권한 이 있어야 합니다.

권한

오브젝트

참고

CREATE VIEW

스키마

SELECT

테이블, 외부 테이블, 뷰

뷰 정의에서 쿼리된 모든 테이블 및/또는 뷰에 필요합니다.

APPLY

마스킹 정책, 행 액세스 정책, 태그

뷰 생성 시 마스킹 정책, 행 액세스 정책, 오브젝트 태그 또는 이러한 거버넌스 기능의 조합을 적용할 때만 필요합니다.

OWNERSHIP

스키마에 이미 존재하는 오브젝트와 동일한 이름을 가진 임시 오브젝트를 생성하려면 오브젝트에 대한 OWNERSHIP 권한을 역할에 부여하거나 상속해야 합니다.

Note that in a managed access schema, only the schema owner (i.e. the role with the OWNERSHIP privilege on the schema) or a role with the MANAGE GRANTS privilege can grant or revoke privileges on objects in the schema, including future grants.

스키마의 모든 오브젝트에 대해 작업하려면 상위 데이터베이스 및 스키마에 대한 USAGE 권한도 필요합니다.

지정된 권한 세트로 사용자 지정 역할을 만드는 방법에 대한 지침은 사용자 지정 역할 만들기 섹션을 참조하십시오.

보안 오브젝트 에 대해 SQL 작업을 수행하기 위한 역할과 권한 부여에 대한 일반적인 정보는 액세스 제어의 개요 섹션을 참조하십시오.

사용법 노트

  • 뷰 정의는 ORDER BY 절(예: create view v1 as select * from t1 ORDER BY column1)을 포함할 수 있습니다. 하지만 Snowflake는 대부분의 뷰 정의에서 ORDER BY 절을 제외 할 것을 권장합니다. 뷰가 정렬을 활용하지 않는 컨텍스트에서 사용되는 경우 ORDER BY 절은 불필요한 비용을 더합니다. 예를 들어, 뷰가 조인에 사용되고 조인 열이 ORDER BY 열과 같지 않을 때는 뷰의 결과를 정렬하는 데 드는 추가 비용이 일반적으로 낭비됩니다. 쿼리 결과를 정렬해야 할 경우에는 보통 뷰 자체보다는 뷰를 사용하는 쿼리에 ORDER BY 를 지정하는 것이 더 효율적입니다.

  • 뷰 정의는 95KB로 제한됩니다.

  • 중첩 수준은 최대 20으로 제한됩니다. 20회 이상 중첩된 뷰를 생성하려는 시도는 실패하게 됩니다.

  • 뷰 정의는 동적이지 않습니다. 기본 원본이 수정되어 더 이상 뷰 정의와 일치하지 않는 경우, 특히 열이 삭제될 때는 뷰가 자동으로 업데이트되지 않습니다. 예:

    • 원본 테이블의 특정 열을 참조하는 뷰가 생성되고 이후에 열이 테이블에서 삭제됩니다.

    • 테이블의 SELECT * 를 사용하여 뷰가 생성되고 다음과 같이 테이블의 열이 변경됩니다.

      • 열이 삭제됩니다.

      • 열이 추가됩니다.

      • 열 순서가 바뀝니다.

    이러한 시나리오에서는 뷰를 쿼리하면 열 관련 오류가 반환됩니다.

  • 뷰의 원본 테이블이 삭제된 경우 뷰를 쿼리하면 object does not exist 오류가 반환됩니다.

  • 스키마는 이름이 같은 테이블과 뷰를 포함할 수 없습니다. CREATE VIEW 문은 이름이 같은 테이블이 스키마에 이미 있으면 오류를 발생시킵니다.

  • 뷰가 생성될 때 테이블과 기타 데이터베이스 오브젝트에 대해 정규화되지 않은 참조는 세션의 현재 스키마가 아니라 뷰의 스키마에서 확인됩니다. 마찬가지로, 부분적으로 정규화된 오브젝트(즉, schema.object)는 세션의 현재 데이터베이스가 아니라 뷰의 데이터베이스에서 확인됩니다.

    SEARCH_PATH 세션 매개 변수(있는 경우)는 무시됩니다.

  • OR REPLACE 를 사용하는 것은 기존 뷰에서 DROP VIEW 를 사용한 다음 같은 이름으로 새 뷰를 만드는 것과 같습니다.

    CREATE OR REPLACE <오브젝트> 문은 원자성입니다. 즉, 오브젝트가 바뀔 때 단일 트랜잭션으로 이전 오브젝트가 삭제되고 새 오브젝트가 생성됩니다.

    이는 CREATE OR REPLACE VIEW 작업과 동시에 수행되는 모든 쿼리가 이전 또는 새 뷰 버전을 사용함을 의미합니다.

    뷰를 다시 만들거나 바꾸면 그 변경 데이터가 삭제되어 뷰의 모든 스트림을 부실하게 만듭니다. 부실 상태의 스트림은 읽을 수 없습니다.

  • COPY GRANTS 사용하기:

    • 데이터 공유:

      • 기존 보안 뷰를 다른 계정과 공유한 경우 대체 뷰도 공유됩니다.

      • 기존 보안 뷰를 데이터 컨슈머로서 자신의 계정과 공유하고 (부모 데이터베이스에서 GRANT IMPORTED PRIVILEGES를 사용하여) 계정의 다른 역할에 추가로 액세스 권한을 부여한 경우 대체 뷰에도 액세스 권한이 부여됩니다.

    • 대체 뷰에 대한 SHOW GRANTS 출력에는 CREATE VIEW 문이 실행될 때 현재 타임스탬프를 포함한 이 문을 실행한 역할로서 복사된 권한의 피부여자가 나열됩니다.

  • 뷰를 만든 다음 해당 뷰에 대한 권한을 역할에 부여하면 해당 역할은 뷰가 액세스하는 기본 테이블에 대한 권한이 없더라도 뷰를 사용할 수 있습니다. 이는 테이블의 서브세트에만 역할 액세스 권한을 부여하는 뷰를 만들 수 있다는 뜻입니다. 예를 들어, 같은 테이블에서 의료 진단 정보가 아니라 의료 청구 정보에 액세스하는 뷰를 만들 수 있습니다. 그런 다음, 해당 뷰에서 회계사가 환자의 진단 정보는 보지 않고 청구 정보를 볼 수 있도록 하는 권한을 《회계사》 역할에 부여할 수 있습니다.

  • SHOW VIEWS 명령은 보안 뷰에 대한 정보를 제공하지 않도록 되어 있습니다. 보안 뷰에 대한 정보를 보려면 Information Schema에서 VIEWS 뷰와 뷰를 소유한 역할을 사용해야 합니다.

  • 재귀 뷰는 열 이름 목록을 제공해야 합니다.

  • 재귀 뷰를 정의할 때 무한 재귀를 방지하십시오. 재귀 뷰 정의에서 WHERE 절을 사용하면 일반적으로 데이터 계층 구조의 마지막 수준을 처리한 후 데이터가 부족해져 결국에는 재귀를 중지할 수 있습니다.

  • 메타데이터 관련:

    주의

    고객은 Snowflake 서비스를 사용할 때 개인 데이터(사용자 오브젝트 제외), 민감한 데이터, 수출 통제 대상 데이터 또는 기타 규제 데이터가 메타데이터로 입력되지 않도록 해야 합니다. 자세한 내용은 Snowflake의 메타데이터 필드 섹션을 참조하십시오.

  • 하나 이상의 뷰 열에 마스킹 정책을 사용하여 뷰를 생성하거나, 뷰에 추가된 행 액세스 정책을 생성할 때, POLICY_CONTEXT 함수를 사용하여, 마스킹 정책에 의해 보호되는 열 및 행 액세스 정책에 의해 보호되는 뷰에 대한 쿼리를 시뮬레이션합니다.

  • 동일한 역할이 뷰와 원본 스트림을 모두 소유하지 않는 한 스트림을 원본 오브젝트로 사용하여 뷰를 만들지 마십시오(즉, 동일한 역할 또는 역할 계층 구조에서 더 낮은 역할이 뷰 및 원본 스트림에 대한 OWNERSHIP 권한을 가짐). 대신 원본 오브젝트로 추적할 오브젝트가 있는 뷰를 만듭니다. 그런 다음 해당 뷰에서 스트림을 만듭니다. 자세한 내용은 Streams on Views 섹션을 참조하십시오.

포팅 참고 사항

  • 일부 벤더는 FORCE 키워드를 지원합니다.

    CREATE OR REPLACE FORCE VIEW ...
    
    Copy

    Snowflake는 FORCE 키워드를 허용하긴 하지만 지원하지는 않습니다. 다시 말해, 이 키워드를 사용하면 구문 오류가 발생하지는 않지만 기본 데이터베이스 오브젝트(테이블 또는 뷰)가 이미 존재하지 않을 경우에 FORCE 를 사용하더라도 뷰가 강제로 생성되지는 않습니다. 존재하지 않는 테이블이나 뷰의 뷰를 만들려고 하면 FORCE 키워드를 사용하더라도 오류 메시지가 나타납니다.

  • 뷰에서 테이블을 조회할 때, 일부 벤더는 활성 스키마에서 정규화되지 않은 테이블 이름을 검색합니다. Snowflake는 뷰와 똑같은 스키마에서 정규화되지 않은 테이블 이름을 검색합니다. Snowflake로 포팅할 때는 정규화된 테이블 이름을 사용하도록 뷰를 업데이트해 보십시오.

테이블에서 모든 행을 선택하는 설명과 함께 현재 스키마에서 뷰 만들기:

CREATE VIEW myview COMMENT='Test view' AS SELECT col1, col2 FROM mytable;

SHOW VIEWS;

+---------------------------------+-------------------+----------+---------------+-------------+----------+-----------+--------------------------------------------------------------------------+
| created_on                      | name              | reserved | database_name | schema_name | owner    | comment   | text                                                                     |
|---------------------------------+-------------------+----------+---------------+-------------+----------+-----------+--------------------------------------------------------------------------|
| Thu, 19 Jan 2017 15:00:37 -0800 | MYVIEW            |          | MYTEST1       | PUBLIC      | SYSADMIN | Test view | CREATE VIEW myview COMMENT='Test view' AS SELECT col1, col2 FROM mytable |
+---------------------------------+-------------------+----------+---------------+-------------+----------+-----------+--------------------------------------------------------------------------+
Copy

다음 예는 뷰가 안전하다는 점을 제외하면 이전 예와 같습니다.

CREATE OR REPLACE SECURE VIEW myview COMMENT='Test secure view' AS SELECT col1, col2 FROM mytable;

SELECT is_secure FROM information_schema.views WHERE table_name = 'MYVIEW';
Copy

아래에서는 재귀 뷰를 만드는 두 가지 방법을 보여줍니다.

먼저 테이블을 만들어 로딩합니다.

CREATE OR REPLACE TABLE employees (title VARCHAR, employee_ID INTEGER, manager_ID INTEGER);
Copy
INSERT INTO employees (title, employee_ID, manager_ID) VALUES
    ('President', 1, NULL),  -- The President has no manager.
        ('Vice President Engineering', 10, 1),
            ('Programmer', 100, 10),
            ('QA Engineer', 101, 10),
        ('Vice President HR', 20, 1),
            ('Health Insurance Analyst', 200, 20);
Copy

재귀 CTE를 사용하여 뷰를 만들고 쿼리합니다.

CREATE VIEW employee_hierarchy (title, employee_ID, manager_ID, "MGR_EMP_ID (SHOULD BE SAME)", "MGR TITLE") AS (
   WITH RECURSIVE employee_hierarchy_cte (title, employee_ID, manager_ID, "MGR_EMP_ID (SHOULD BE SAME)", "MGR TITLE") AS (
      -- Start at the top of the hierarchy ...
      SELECT title, employee_ID, manager_ID, NULL AS "MGR_EMP_ID (SHOULD BE SAME)", 'President' AS "MGR TITLE"
        FROM employees
        WHERE title = 'President'
      UNION ALL
      -- ... and work our way down one level at a time.
      SELECT employees.title, 
             employees.employee_ID, 
             employees.manager_ID, 
             employee_hierarchy_cte.employee_id AS "MGR_EMP_ID (SHOULD BE SAME)", 
             employee_hierarchy_cte.title AS "MGR TITLE"
        FROM employees INNER JOIN employee_hierarchy_cte
       WHERE employee_hierarchy_cte.employee_ID = employees.manager_ID
   )
   SELECT * 
      FROM employee_hierarchy_cte
);
Copy
SELECT * 
    FROM employee_hierarchy 
    ORDER BY employee_ID;
+----------------------------+-------------+------------+-----------------------------+----------------------------+
| TITLE                      | EMPLOYEE_ID | MANAGER_ID | MGR_EMP_ID (SHOULD BE SAME) | MGR TITLE                  |
|----------------------------+-------------+------------+-----------------------------+----------------------------|
| President                  |           1 |       NULL |                        NULL | President                  |
| Vice President Engineering |          10 |          1 |                           1 | President                  |
| Vice President HR          |          20 |          1 |                           1 | President                  |
| Programmer                 |         100 |         10 |                          10 | Vice President Engineering |
| QA Engineer                |         101 |         10 |                          10 | Vice President Engineering |
| Health Insurance Analyst   |         200 |         20 |                          20 | Vice President HR          |
+----------------------------+-------------+------------+-----------------------------+----------------------------+
Copy

키워드 RECURSIVE를 사용하여 뷰를 만들고 쿼리합니다.

CREATE RECURSIVE VIEW employee_hierarchy_02 (title, employee_ID, manager_ID, "MGR_EMP_ID (SHOULD BE SAME)", "MGR TITLE") AS (
      -- Start at the top of the hierarchy ...
      SELECT title, employee_ID, manager_ID, NULL AS "MGR_EMP_ID (SHOULD BE SAME)", 'President' AS "MGR TITLE"
        FROM employees
        WHERE title = 'President'
      UNION ALL
      -- ... and work our way down one level at a time.
      SELECT employees.title, 
             employees.employee_ID, 
             employees.manager_ID, 
             employee_hierarchy_02.employee_id AS "MGR_EMP_ID (SHOULD BE SAME)", 
             employee_hierarchy_02.title AS "MGR TITLE"
        FROM employees INNER JOIN employee_hierarchy_02
        WHERE employee_hierarchy_02.employee_ID = employees.manager_ID
);
Copy
SELECT * 
    FROM employee_hierarchy_02 
    ORDER BY employee_ID;
+----------------------------+-------------+------------+-----------------------------+----------------------------+
| TITLE                      | EMPLOYEE_ID | MANAGER_ID | MGR_EMP_ID (SHOULD BE SAME) | MGR TITLE                  |
|----------------------------+-------------+------------+-----------------------------+----------------------------|
| President                  |           1 |       NULL |                        NULL | President                  |
| Vice President Engineering |          10 |          1 |                           1 | President                  |
| Vice President HR          |          20 |          1 |                           1 | President                  |
| Programmer                 |         100 |         10 |                          10 | Vice President Engineering |
| QA Engineer                |         101 |         10 |                          10 | Vice President Engineering |
| Health Insurance Analyst   |         200 |         20 |                          20 | Vice President HR          |
+----------------------------+-------------+------------+-----------------------------+----------------------------+
Copy