SQL을 사용하여 오브젝트 설명 자동 생성

Cortex 기반 오브젝트 설명 기능을 사용하면 :doc:`Snowflake Cortex COMPLETE 함수</sql-reference/functions/complete-snowflake-cortex>`를 사용하여 테이블, 뷰, 열에 대한 설명을 자동으로 생성할 수 있습니다. 이 기능은 Snowflake에서 호스팅하는 대규모 언어 모델(LLMs)을 활용하여 오브젝트 메타데이터를 평가하고, 원하는 경우 샘플 데이터를 사용하여 설명을 생성합니다.

이 항목에서는 저장 프로시저를 사용하여 프로그래밍 방식으로 설명을 생성하는 방법에 대해 설명합니다. |sf-web-interface|를 사용하여 설명을 생성하는 방법에 대한 자세한 내용은 Snowflake Cortex로 설명 생성하기 섹션을 참조하세요.

설명 생성하기

AI_GENERATE_TABLE_DESC 저장 프로시저는 테이블과 뷰에 대한 설명을 자동으로 생성합니다. 또한 해당 테이블 또는 뷰의 열에 대한 설명을 생성할 수도 있습니다.

AI_GENERATE_TABLE_DESC 저장 프로시저는 두 개의 인자를 허용합니다.

  • 설명을 생성할 테이블 또는 뷰의 이름.

  • 다음을 수행할 수 있는 선택적 구성 오브젝트.

    • 지정된 테이블 또는 뷰의 열에 대한 설명을 생성합니다.

    • 테이블 또는 뷰의 샘플 데이터를 사용하여 열 설명의 정확도를 잠재적으로 개선할 수 있습니다.

예: 테이블 설명 생성하기
CALL AI_GENERATE_TABLE_DESC( 'my_table');
Copy
예: 샘플 데이터를 사용하지 않고 테이블 및 열 설명 생성하기
CALL AI_GENERATE_TABLE_DESC(
  'mydb.sch1.hr_data',
  {
    'describe_columns': true,
    'use_table_data': false
  });
Copy
예: 정확도 향상을 위해 샘플 데이터를 사용하여 뷰 및 열 설명 생성하기
CALL AI_GENERATE_TABLE_DESC(
  'mydb.sch1.v1',
  {
    'describe_columns': true,
    'use_table_data': true
  });
Copy

저장 프로시저의 전체 구문은 AI_GENERATE_TABLE_DESC 섹션을 참조하세요.

응답 작업하기

AI_GENERATE_TABLE_DESC 저장 프로시저는 테이블 및 열에 대한 일반 정보와 함께 생성된 설명이 포함된 JSON 오브젝트를 반환합니다. 이 오브젝트 내에서 description 필드에는 생성된 설명이 포함됩니다.

다음 테이블을 생성했다고 가정해 보겠습니다.

CREATE OR REPLACE TABLE mydb.sch1.hr_data (fname VARCHAR, age INTEGER);

INSERT INTO hr_data (fname, age)
    VALUES
        ('Thomas',    44),
        ('Katherine', 29),
        ('Lisa',      29);
Copy

이 테이블에서, AI_GENERATE_TABLE_DESC에서 반환된 JSON 오브젝트의 예제는 다음과 같습니다.

{
  "COLUMNS": [
    {
      "database_name": "mydb",
      "description": "The first name of the employee.",
      "name": "FNAME",
      "schema_name": "sch1",
      "table_name": "hr_data"
    }
    {
      "database_name": "mydb",
      "description": "A column holding data of type DecimalType representing age values.",
      "name": "AGE",
      "schema_name": "sch1",
      "table_name": "hr_data"
    },
  ],
  "TABLE": [
    {
      "database_name": "mydb",
      "description": " The table contains records of employee data, specifically demographic information. Each record includes an employee's age and name.",
      "name": "hr_data",
      "schema_name": "sch1"
    }
  ]
}

각 JSON 필드에 대한 자세한 내용은 반환 섹션을 참조하세요.

생성된 설명을 주석으로 설정하기

생성된 설명을 테이블, 뷰 또는 열에 대한 설명으로 설정하려면 SET COMMENT 매개 변수가 포함된 SQL 문을 수동으로 실행해야 합니다. 예를 들어, 테이블 ``t1``에 대해 생성된 설명을 저장하려면 ``ALTER TABLE t1 SET COMMENT = ‘ai generated description’;``을 실행합니다.

사용자 지정 코드를 작성하여 설명을 자동으로 생성하고 저장할 수 있습니다. 이 작업을 수행하는 저장 프로시저의 예제는 섹션을 참조하세요.

액세스 제어 요구 사항

AI_GENERATE_TABLE_DESC 저장 프로시저를 호출하려면 사용자에게 다음 권한과 역할이 있어야 합니다.

  • 테이블 또는 뷰에 대한 SELECT 권한.

  • SNOWFLAKE.CORTEX_USER 데이터베이스 역할.

기능의 가용성

해당 리전은 설명을 생성하기 위해 Snowflake Cortex(예: Mistral-7b 및 Llama 3.1-8b)에서 사용하는 LLM을 지원해야 합니다. :ref:`COMPLETE 함수의 가용성<label-cortex_llm_availability>`을 확인합니다. COMPLETE 함수가 사용자의 리전에서 지원되지 않는 경우 :doc:`리전 간 추론</user-guide/snowflake-cortex/cross-region-inference>`을 활성화해야 이 기능을 사용할 수 있습니다.

샘플 데이터 사용하기

열에 대한 설명을 생성할 때 메타데이터에만 의존하거나 샘플 데이터를 사용하여 Snowflake Cortex Powered Description을 개선하도록 선택할 수 있습니다. 샘플 데이터는 특정 열 내의 데이터로, 설명을 생성하기 위해 Snowflake Cortex를 사용할 때 평가되는 데이터를 말합니다. 샘플 데이터를 사용하도록 선택하면 Snowflake가 샘플 데이터의 일부를 사용하여 설명을 생성하므로 더 정확한 설명이 생성됩니다. 샘플 데이터는 Snowflake에 사용량 데이터로 저장되지 않습니다.

비용 고려 사항

설명을 생성하는 데는 다음과 같은 비용이 발생합니다.

  • 사용 중인 웨어하우스에서 소비한 크레딧입니다.

  • Mistral-7b 및 Llama 3.1-8b와 같이 더 작은 LLMs 에서 Snowflake Cortex를 사용할 경우 크레딧이 청구됩니다. 이러한 요금은 청구서에 AI 서비스로 표시되며, 여기에는 Snowflake Cortex의 모든 사용이 포함됩니다.

제한 사항

열이 1,000개를 초과하는 오브젝트에 대해서는 열 설명을 생성할 수 없습니다.

예: 설명을 생성하고 주석으로 설정하기

1단계: 저장 프로시저 만들기

다음 저장 프로시저는 다음을 수행합니다.

  • 스키마의 모든 테이블(및 해당 열)에 대한 설명을 자동으로 생성합니다.

  • 이러한 설명을 테이블과 열에 대한 주석으로 설정합니다.

CREATE OR REPLACE PROCEDURE DESCRIBE_TABLES_SET_COMMENT (database_name STRING, schema_name STRING,
  set_table_comment BOOLEAN,
  set_column_comment BOOLEAN)
  RETURNS STRING
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.10'
  PACKAGES=('snowflake-snowpark-python','joblib')
  HANDLER = 'main'
AS
$$
import json
from joblib import Parallel, delayed
import multiprocessing

def generate_descr(session, database_name, schema_name, table, set_table_comment, set_column_comment):
  table_name =  table['TABLE_NAME']
  async_job = session.sql(f"CALL AI_GENERATE_TABLE_DESC( '{database_name}.{schema_name}.{table_name}',{{'describe_columns': true, 'use_table_data': true}})").collect_nowait()
  result = async_job.result()
  output = json.loads(result[0][0])
  columns_ret = output["COLUMNS"]
  table_ret = output["TABLE"][0]

  table_description = table_ret["description"]
  table_name = table_ret["name"]
  database_name = table_ret["database_name"]
  schema_name = table_ret["schema_name"]

  if (set_table_comment):
      table_description = table_description.replace("'", "\\'")
      session.sql(f"""ALTER TABLE {database_name}.{schema_name}.{table_name} SET COMMENT = '{table_description}'""").collect()

  for column in columns_ret:
      column_description = column["description"];
      column_name = column["name"];
      if not column_name.isupper():
        column_name = '"' + column_name + '"'

      if (set_column_comment):
          column_description = column_description.replace("'", "\\'")
          session.sql(f"""ALTER TABLE  {database_name}.{schema_name}.{table_name} MODIFY COLUMN {column_name}  COMMENT '{column_description}'""").collect()

  return 'Success';

def main(session, database_name, schema_name, set_table_comment, set_column_comment):

    schema_name = schema_name.upper()
    database_name = database_name.upper()
    tablenames = session.sql(f"""SELECT table_name
                      FROM {database_name}.information_schema.tables
                      WHERE table_schema = '{schema_name}'
                      AND table_type = 'BASE TABLE'""").collect()
    try:
        Parallel(n_jobs=multiprocessing.cpu_count(), backend="threading")(
                delayed(generate_descr)(
                    session,
                    database_name,
                    schema_name,
                    table,
                    set_table_comment,
                    set_column_comment,
                ) for table in tablenames
            )
        return 'Success'
    except Exception as e:
        # Catch and return the error message
        return f"An error occurred: {str(e)}"
$$;
Copy

2단계: 저장 프로시저 호출

스키마의 이름이 ``my_db.sch1``이라고 가정하고 다음과 같이 저장 프로시저를 호출하여 테이블과 열에 대한 설명을 생성합니다.

CALL describe_tables_set_comment('my_db', 'sch1', true, true);
Copy

DESC TABLE 명령을 실행하여 생성된 설명이 테이블에 대한 주석으로 설정되었는지 확인합니다.

예: 설명 생성 및 카탈로그 테이블에 저장

1단계: 저장 프로시저 만들기

다음 저장 프로시저는 다음을 수행합니다.

  • 스키마의 모든 테이블(및 해당 열)에 대한 설명을 자동으로 생성합니다.

  • 카탈로그 테이블을 채웁니다. 여기서 각 행은 생성된 설명이 있는 테이블 또는 열을 나타냅니다.

CREATE OR REPLACE PROCEDURE DESCRIBE_TABLES_SET_CATALOG (database_name string, schema_name string, catalog_table string)
  RETURNS STRING
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.10'
  PACKAGES=('snowflake-snowpark-python','joblib')
  HANDLER = 'main'
AS
$$
import json
from joblib import Parallel, delayed
import multiprocessing

def generate_descr(session, database_name, schema_name, table, catalog_table):
    table_name =  table['TABLE_NAME']
    async_job = session.sql(f"CALL AI_GENERATE_TABLE_DESC( '{database_name}.{schema_name}.{table_name}',{{'describe_columns': true, 'use_table_data': true}})").collect_nowait()
    result = async_job.result()
    output = json.loads(result[0][0])
    columns_ret = output["COLUMNS"]
    table_ret = output["TABLE"][0]

    table_description = table_ret["description"]
    table_description = table_description.replace("'", "\\'")
    table_name = table_ret["name"]
    database_name = table_ret["database_name"]
    schema_name = table_ret["schema_name"]

    session.sql(f"""INSERT INTO {catalog_table} (domain, description, name, database_name, schema_name, table_name)
                          VALUES ('TABLE', '{table_description}', '{table_name}', '{database_name}', '{schema_name}', null)""").collect()

    for column in columns_ret:
        column_description = column["description"];
        column_description = column_description.replace("'", "\\'")
        column_name = column["name"];
        if not column_name.isupper():
            column_name = '"' + column_name + '"'
        session.sql(f"""INSERT INTO {catalog_table} (domain, description, name, database_name, schema_name, table_name)
                          VALUES ('COLUMN', '{column_description}', '{column_name}', '{database_name}', '{schema_name}', '{table_name}')""").collect()

    return 'Success';

def main(session, database_name, schema_name, catalog_table):

    schema_name = schema_name.upper()
    database_name = database_name.upper()
    catalog_table_upper = catalog_table.upper()
    tablenames = session.sql(f"""SELECT table_name
                      FROM {database_name}.information_schema.tables
                      WHERE table_schema = '{schema_name}'
                      AND table_type = 'BASE TABLE'
                      AND table_name !='{catalog_table_upper}'""").collect()
    try:
        Parallel(n_jobs=multiprocessing.cpu_count(), backend="threading")(
                delayed(generate_descr)(
                    session,
                    database_name,
                    schema_name,
                    table,
                    catalog_table,
                ) for table in tablenames
            )
        return 'Success'
    except Exception as e:
        # Catch and return the error message
        return f"An error occurred: {str(e)}"
$$;
Copy

2단계: 채울 카탈로그 테이블 만들기

다음 코드를 사용하여 테이블 및 열 설명이 저장되는 카탈로그 테이블을 생성합니다.

CREATE OR REPLACE TABLE catalog_table (
  domain VARCHAR,
  description VARCHAR,
  name VARCHAR,
  database_name VARCHAR,
  schema_name VARCHAR,
  table_name VARCHAR
  );
Copy

3단계: 저장 프로시저 호출

스키마의 이름이 ``my_db.sch1``이라고 가정하고 다음과 같이 저장 프로시저를 호출합니다.

CALL describe_tables_set_catalog('my_db', 'sch1', 'catalog_table');
Copy