사용자 정의 유형

기존 :doc:`Snowflake 데이터 타입 </sql-reference-data-types>`을 기반으로 하는 새로운 데이터 타입인 *사용자 정의 유형*을 정의할 수 있습니다. 예를 들어, 사람의 나이에 대한 열을 정의하고 소수점 이하 자릿수가 없고 최대 3자리 숫자를 포함하도록 값을 제한한다고 가정해 보겠습니다. ``NUMBER(3,0)``에 해당하는 ``age``라는 데이터 타입을 정의할 수 있습니다.

사용자 정의 유형은 열 정의, 함수 및 프로시저 정의, 캐스트 식 등 해당 유형을 사용할 수 있는 모든 위치에서 사용할 수 있는 스키마 수준 오브젝트입니다.

사용자 정의 유형을 사용하면 스키마 유지 관리를 간소화하고 데이터 품질을 개선할 수 있습니다. 사용자 정의 유형을 한 번 정의한 다음 여러 오브젝트에서 사용할 수 있습니다.

필드에 여러 열이나 테이블을 사용하는 대신 사용자 정의 유형을 사용하여 관련 데이터 필드를 단일 논리 열로 그룹화할 수도 있습니다. 예를 들어, 도로 주소, 도시, 주, ZIP 코드에 대한 필드가 있는 :ref:`정형 OBJECT 타입<label-structured_types_specifying_object>`인 주소에 대한 데이터 타입을 정의할 수 있습니다.

사용자 정의 유형에 필요한 권한

스키마에서 사용자 정의 유형을 생성하려면 해당 스키마에 대한 CREATE TYPE 권한이 부여된 역할을 사용해야 합니다.

자세한 내용은 :ref:`사용자 정의 유형에 대한 액세스 제어 요구 사항 <label-access_control_privileges_type>`을 참조하세요.

사용자 정의 유형에 대한 일반 사용법 노트

  • 사용자 정의 유형의 정의를 변경하려면 해당 유형을 삭제하고 다시 생성합니다.

    사용자 정의 유형의 정의를 변경하는 경우:

    • 해당 유형을 사용하는 테이블 열에서 직접 작동하는 SQL 문은 SELECT 문 및 DML 문을 포함한 오류를 반환할 수 있습니다. 그러나 해당 유형을 사용하는 테이블 열에서 직접 작동하지 않는 SQL 문은 정상적으로 실행됩니다. 예를 들어, 테이블에 이름이 ``typed_column``인 사용자 정의 유형 열이 포함되고 SELECT 문이 해당 SELECT 목록의 다른 열을 지정하는 경우 SELECT 문이 정상적으로 실행됩니다. 문제를 해결하기 위해 SQL 문을 수정하여 기본 Snowflake 유형을 사용할 수 있습니다.

    • 해당 유형을 사용하는 함수 및 저장 프로시저에 대한 호출은 오류를 반환합니다. 문제를 해결하려면 함수와 저장 프로시저를 삭제하고 다시 생성합니다.

  • ALTER TABLE … ALTER COLUMN 명령은 열의 데이터 타입을 사용자 정의 유형에서 호환 가능한 :doc:`Snowflake 데이터 타입 </sql-reference-data-types>`으로 또는 Snowflake 데이터 타입에서 사용자 정의 유형으로 변경할 수 있습니다.

  • OBJECT_CONSTRUCT 함수 또는 :ref:`OBJECT 상수 <label-object_constant>`를 사용하여 사용자 정의 유형의 열에 삽입할 오브젝트를 구성하는 경우, 결과를 사용자 정의 유형으로 캐스팅합니다.

    예는 테이블 열에 사용자 정의 유형 사용하기 을 참조하십시오.

  • :doc:`세트 연산자 </sql-reference/operators-query>`(예:UNION, INTERSECT, EXCEPT) 또는 :doc:`조건식 함수 </sql-reference/expressions-conditional>`(예: CASE, IFF, COALESCE, NVL 등)가 사용자 정의 유형의 값으로 확인되는 식을 평가하는 경우, Snowflake는 피연산자의 기본 유형을 사용하여 공통 유형을 결정합니다. 기본적으로, 결과의 데이터 타입은 이 기본 유형입니다. 결과가 사용자 정의 유형의 값이 되도록 하려면 최종 식을 사용자 정의 유형으로 명시적으로 캐스팅합니다.

    사용자 정의 유형이 세트 연산 또는 조건식 함수에서 사용될 때 다음 규칙이 적용됩니다.

    • 사용자 정의 유형은 기본 유형과 구분되지만, 식 유형 확인에서는 공통 유형을 찾기 위해 기본 유형으로 강제 변환됩니다.

    • 분기 또는 피연산자가 단일 Snowflake 유형(예: VARCHAR 또는 NUMBER)으로 해석되는 경우, 해당 유형이 결과 유형이 됩니다.

    • 사용자 정의 유형을 보존하거나 사용자 정의 유형의 값인 결과를 생성하려면 CAST(expr AS user-defined type) 또는 :samp:`{expr}::{user-defined type}`을 사용하여 전체 식을 :ref:`캐스팅 <label-user_defined_type_casting_explicit>`합니다.

    • 호환되지 않는 기본 유형(예: VARCHAR 및 NUMBER)은 일반 :doc:`강제 변환 규칙 </sql-reference/data-type-conversion>`을 따릅니다. 공통 기본 유형이 없으면 오류가 반환됩니다.

    예는 사용자 정의 유형에 세트 연산자 및 조건식 함수 사용하기 을 참조하십시오.

  • 함수 :ref:`오버로딩 <label-procedure_function_name_overloading>`에 사용자 정의 유형 및 호환되는 Snowflake 데이터 타입 사용이 허용됩니다. 즉, 함수 인자 유형에 대해 사용자 정의 유형을 지정할 수 있으며, 이름이 같은 함수의 인자 유형에 대해 호환되는 Snowflake 데이터 타입을 지정할 수 있습니다.

  • 사용자 정의 유형이 SQL 사용자 정의 함수(UDF) 또는 Snowflake Scripting 저장 프로시저의 RETURN 유형으로 지정된 경우, 반환 값은 UDF 또는 저장 프로시저의 본문에서 사용자 정의 유형으로 명시적으로 캐스팅되어야 합니다.

  • 사용자 정의 유형이 SQL 이외의 언어(예: Python 또는 Java)로 작성된 UDF 또는 프로시저에 대한 인자나 반환 값으로 사용되는 경우, 사용자 정의 유형은 기본 유형과 동일하게 처리됩니다.

  • :doc:`스키마 진화 </user-guide/data-load-schema-evolution>`는 사용자 정의 유형에 지원되지 않습니다.

사용자 정의 유형 캐스팅하기

사용자 정의 유형은 명시적 캐스팅 및 암시적 캐스팅(강제 변환)을 모두 포함하여 :doc:`데이터 타입 변환 </sql-reference/data-type-conversion>`을 지원합니다.

사용자 정의 유형으로의 명시적 캐스팅 및 사용자 정의 유형에서의 명시적 캐스팅

사용자 정의 유형 값은 해당 기본 유형의 값과 동일한 데이터 타입으로 캐스팅될 수 있습니다. 예를 들어, NUMBER 유형을 기반으로 하는 ``age``라는 사용자 정의 유형을 생성합니다.

CREATE TYPE age AS NUMBER(3,0);

값을 사용자 정의 유형의 기본 유형으로 캐스팅할 수 있는 경우 해당 값을 사용자 정의 유형으로 캐스팅할 수 있습니다. 예를 들어, 10 값은 NUMBER 유형으로 캐스팅할 수 있으므로 해당 값을 age 유형으로 캐스팅할 수 있습니다.

SELECT 10::age;

사용자 정의 유형의 기본 유형을 해당 데이터 타입으로 캐스팅할 수 있는 경우 사용자 정의 유형 값을 다른 데이터 타입으로 캐스팅할 수 있습니다. 예를 들어, NUMBER 값은 VARCHAR 유형으로 캐스팅할 수 있으므로 사용자 정의 유형 age``의 ``10 값은 VARCHAR 유형으로 캐스팅할 수 있습니다.

SELECT 10::age::VARCHAR;

사용자 정의 유형 강제 변환

사용자 정의 유형 값은 기본 유형으로 강제 변환됩니다. 따라서 모든 작업에서 기본 유형과 동일하게 동작합니다. 예를 들어, NUMBER 유형을 기반으로 하는 age``라는 사용자 정의 유형 ``age 유형의 두 열이 있는 테이블을 생성합니다.

CREATE TYPE age AS NUMBER(3,0);

CREATE TABLE test_age_udf(a age, b age);

테이블에 값을 삽입합니다.

INSERT INTO test_age_udf VALUES (10, 20);

다음 예제에서는 테이블 값에 대해 더하기 연산을 수행하며, Snowflake는 age 값을 NUMBER 유형의 값으로 강제 변환하여 연산을 완료합니다. 이 예제에서는 SYSTEM$TYPEOF 함수를 사용하여 결과의 데이터 타입을 표시합니다.

SELECT a + b AS result,
       SYSTEM$TYPEOF(a + b) AS type
  FROM test_age_udf;
+--------+------------------+
| RESULT | TYPE             |
|--------+------------------|
|     30 | NUMBER(4,0)[SB1] |
+--------+------------------+

사용자 정의 데이터 타입의 예제

다음 예제에서는 사용자 정의 유형을 사용하는 방법을 보여줍니다.

테이블 열에 사용자 정의 유형 사용하기

다음 예제에서는 ``address``라는 사용자 정의 유형을 생성한 후 테이블에서 해당 유형을 사용합니다.

  1. :ref:`정형 OBJECT 유형 <label-structured_types_specifying_object>`을 기반으로 하는 사용자 정의 유형을 생성하여 주소 정보를 저장합니다.

    CREATE TYPE address AS OBJECT(
      street VARCHAR(100),
      city VARCHAR(50),
      state_abbr CHAR(2),
      zip_code CHAR(10)
    );
    
  2. 주소를 포함한 고객 정보를 저장하는 테이블을 생성합니다.

    CREATE TABLE customers_udt_test (
      cust_id VARCHAR NOT NULL,
      cust_name VARCHAR(100),
      cust_address address
    );
    
  3. 테이블에 행을 삽입하고 OBJECT 상수 <label-object_constant>`를 ``address` 유형으로 캐스팅하여 cust_address 열의 값을 지정합니다.

    INSERT INTO customers_udt_test (cust_id, cust_name, cust_address)
      SELECT
        '1000',
        'Example1 Inc',
        {
          'street': '101 Snow Street',
          'city': 'San Francisco',
          'state_abbr': 'CA',
          'zip_code': '94102'
        }::address;
    
  4. 테이블에 행을 삽입하고 OBJECT_CONSTRUCT 함수를 호출한 후 반환 값을 address 유형으로 캐스팅하여 cust_address 열의 값을 지정합니다.

    INSERT INTO customers_udt_test (cust_id, cust_name, cust_address)
      SELECT
        '1001',
        'Example2 Inc',
        OBJECT_CONSTRUCT(
          'street', '555 Polar Bear Street',
          'city', 'New York',
          'state_abbr', 'NY',
          'zip_code', '10001'
        )::address;
    
  5. 테이블에 행을 삽입하고 OBJECT 상수를 address 유형의 기본 유형인 OBJECT 유형으로 캐스팅하여 cust_address 열의 값을 지정합니다. 일반적으로 OBJECT 상수를 사용자 정의 유형으로 캐스팅하는 것이 더 쉽지만, 이 예제에서는 OBJECT 상수가 사용자 정의 유형으로 강제 변환되는 것을 보여줍니다.

    INSERT INTO customers_udt_test (cust_id, cust_name, cust_address)
      SELECT
        '1002',
        'Example3 Inc',
        {
          'street': '909 Flake Street',
          'city': 'Seattle',
          'state_abbr': 'WA',
          'zip_code': '98109'
        }::OBJECT(
             street VARCHAR(100),
             city VARCHAR(50),
             state_abbr CHAR(2),
             zip_code CHAR(10));
    
  6. 삽입된 행을 표시하려면 테이블을 쿼리합니다.

    SELECT * FROM customers_udt_test;
    
    +---------+--------------+--------------------------------------+
    | CUST_ID | CUST_NAME    | CUST_ADDRESS                         |
    |---------+--------------+--------------------------------------|
    | 1000    | Example1 Inc | {                                    |
    |         |              |   "street": "101 Snow Street",       |
    |         |              |   "city": "San Francisco",           |
    |         |              |   "state_abbr": "CA",                |
    |         |              |   "zip_code": "94102"                |
    |         |              | }                                    |
    | 1001    | Example2 Inc | {                                    |
    |         |              |   "street": "555 Polar Bear Street", |
    |         |              |   "city": "New York",                |
    |         |              |   "state_abbr": "NY",                |
    |         |              |   "zip_code": "10001"                |
    |         |              | }                                    |
    | 1002    | Example3 Inc | {                                    |
    |         |              |   "street": "909 Flake Street",      |
    |         |              |   "city": "Seattle",                 |
    |         |              |   "state_abbr": "WA",                |
    |         |              |   "zip_code": "98109"                |
    |         |              | }                                    |
    +---------+--------------+--------------------------------------+
    
  7. 테이블을 쿼리하고 콜론 연산자를 사용하여 address 데이터에 zip_code 값만 표시합니다.

    SELECT cust_id,
           cust_name,
           cust_address:zip_code
      FROM customers_udt_test;
    
    +---------+--------------+-----------------------+
    | CUST_ID | CUST_NAME    | CUST_ADDRESS:ZIP_CODE |
    |---------+--------------+-----------------------|
    | 1000    | Example1 Inc | 94102                 |
    | 1001    | Example2 Inc | 10001                 |
    | 1002    | Example3 Inc | 98109                 |
    +---------+--------------+-----------------------+
    

사용자 정의 유형에 세트 연산자 및 조건식 함수 사용하기

세트 연산자 또는 조건식 함수 </sql-reference/expressions-conditional>`로 Snowflake 유형 및 사용자 정의 유형의 값을 평가하는 경우 해당 유형이 호환되고 단일 유형으로 강제 변환될 수 있어야 합니다. 결과 출력은 사용자 정의 유형으로 명시적으로 캐스팅되지 않는 한 Snowflake 기본 유형입니다. 자세한 내용은 :ref:`label-user_defined_type_usage_notes 섹션을 참조하십시오.

이 섹션의 예제에서는 사용자 정의 유형에 세트 연산자 및 조건식을 사용합니다. 먼저, 다양한 기본 유형으로 여러 사용자 정의 유형을 생성합니다.

CREATE TYPE us_zipcode AS VARCHAR;
CREATE TYPE uk_postcode AS VARCHAR;
CREATE TYPE positive_integer AS INTEGER;
CREATE TYPE positive_number AS NUMBER;

다음 쿼리는 IFF 함수를 호출합니다. 호출은 us_zipcode 사용자 정의 유형 및 호환되는 Snowflake 유형의 값을 평가합니다. 쿼리는 SYSTEM$TYPEOF 함수를 사용하여 결과가 Snowflake 기본 유형 VARCHAR임을 보여줍니다.

SELECT IFF(TRUE, '90210'::us_zipcode, '10006') AS result,
       SYSTEM$TYPEOF(IFF(TRUE, '90210'::us_zipcode, '10006')) AS type;
+--------+-------------------------+
| RESULT | TYPE                    |
|--------+-------------------------|
| 90210  | VARCHAR(134217728)[LOB] |
+--------+-------------------------+

다음 쿼리는 이전 쿼리와 동일하지만, 결과를 us_zipcode 사용자 정의 유형으로 캐스팅합니다.

SELECT IFF(TRUE, '90210'::us_zipcode, '10006')::us_zipcode AS result,
       SYSTEM$TYPEOF(IFF(TRUE, '90210'::us_zipcode, '10006')::us_zipcode) AS type;
+--------+-------------------------------+
| RESULT | TYPE                          |
|--------+-------------------------------|
| 90210  | MYDB.MYSCHEMA.US_ZIPCODE[LOB] |
+--------+-------------------------------+

다음 쿼리에는 서로 다르지만 호환되는 사용자 정의 유형을 평가하고 Snowflake 기본 유형의 값을 반환하는 CASE 식이 포함됩니다.

SELECT CASE
         WHEN TRUE THEN 'SW1A 0AA'::uk_postcode
           ELSE '90210'::us_zipcode
         END AS result,
       SYSTEM$TYPEOF(CASE
         WHEN TRUE THEN 'SW1A 0AA'::uk_postcode
           ELSE '90210'::us_zipcode
         END) AS type;
+----------+-------------------------+
| RESULT   | TYPE                    |
|----------+-------------------------|
| SW1A 0AA | VARCHAR(134217728)[LOB] |
+----------+-------------------------+

다음 쿼리는 이전 쿼리와 동일하지만, 결과를 uk_postcode 사용자 정의 유형으로 캐스팅합니다.

SELECT CAST(CASE
         WHEN TRUE THEN 'SW1A 0AA'::uk_postcode
           ELSE '90210'::us_zipcode
         END AS uk_postcode) AS result,
       SYSTEM$TYPEOF(CAST(CASE
         WHEN TRUE THEN 'SW1A 0AA'::uk_postcode
           ELSE '90210'::us_zipcode
         END AS uk_postcode)) AS type;
+----------+--------------------------------------------+
| RESULT   | TYPE                                       |
|----------+--------------------------------------------|
| SW1A 0AA | MYDB.MYSCHEMA.UK_POSTCODE[LOB]             |
+----------+--------------------------------------------+

다음 쿼리에는 서로 다르지만 호환되는 사용자 정의 유형을 평가하고 Snowflake 기본 유형의 값을 반환하는 COALESCE 식이 포함됩니다.

SELECT COALESCE(
         5::positive_integer,
         10::positive_number) AS result,
       SYSTEM$TYPEOF(COALESCE(
         5::positive_integer,
         10::positive_number)) AS type;
+--------+-------------------+
| RESULT | TYPE              |
|--------+-------------------|
|      5 | NUMBER(38,0)[SB1] |
+--------+-------------------+

다음 쿼리는 이전 쿼리와 동일하지만, 결과를 positive_number 사용자 정의 유형으로 캐스팅합니다.

SELECT CAST(COALESCE(
         5::positive_integer,
         10::positive_number
       ) AS positive_number) AS result,
       SYSTEM$TYPEOF(CAST(COALESCE(
         5::positive_integer,
         10::positive_number
       ) AS positive_number)) AS type;
+--------+------------------------------------+
| RESULT | TYPE                               |
|--------+------------------------------------|
|      5 | MYDB.MYSCHEMA.POSITIVE_NUMBER[SB1] |
+--------+------------------------------------+