legacy SQL의 사용자 정의 함수

이 문서에서는 legacy SQL 쿼리 구문에서 자바스크립트 사용자 정의 함수를 사용하는 방법을 자세히 설명합니다. BigQuery의 기본 쿼리 구문은 표준 SQL입니다. 표준 SQL의 사용자 정의 함수에 대한 자세한 내용은 표준 SQL 사용자 정의 함수를 참조하세요.

BigQuery legacy SQL에서는 자바스크립트로 작성된 사용자 정의 함수(UDF)가 지원됩니다. UDF는 MapReduce의 'Map' 함수와 비슷하게, 단일 행을 입력으로 사용하고 0개 이상의 행을 출력으로 생성합니다. 출력은 입력과 다른 스키마를 포함할 수 있습니다.

표준 SQL의 사용자 정의 함수에 대한 자세한 내용은 표준 SQL의 사용자 정의 함수를 참조하세요.

UDF 예시

// UDF definition
function urlDecode(row, emit) {
  emit({title: decodeHelper(row.title),
        requests: row.num_requests});
}

// Helper function with error handling
function decodeHelper(s) {
  try {
    return decodeURI(s);
  } catch (ex) {
    return s;
  }
}

// UDF registration
bigquery.defineFunction(
  'urlDecode',  // Name used to call the function from SQL

  ['title', 'num_requests'],  // Input column names

  // JSON representation of the output schema
  [{name: 'title', type: 'string'},
   {name: 'requests', type: 'integer'}],

  urlDecode  // The function reference
);

맨 위로

UDF 구조

function name(row, emit) {
  emit(<output data>);
}

BigQuery UDF는 테이블 또는 하위 SELECT 쿼리 결과의 개별 행에서 작동합니다. UDF에는 두 가지 공식 매개변수가 있습니다.

  • row: 입력 행입니다.
  • emit: 출력 데이터 수집을 위해 BigQuery에서 사용되는 후크입니다. emit 함수는 출력 데이터의 단일 행을 나타내는 자바스크립트 객체인 하나의 매개변수만 사용합니다. emit 함수는 루프와 같은 곳에서 두 번 이상 호출하여 데이터의 여러 행을 출력할 수 있습니다.

다음 코드 예시는 기본 UDF를 보여줍니다.

function urlDecode(row, emit) {
  emit({title: decodeURI(row.title),
        requests: row.num_requests});
}

UDF 등록

BigQuery SQL에서 함수가 호출될 수 있게 하려면 함수 이름을 등록해야 합니다. 등록 이름은 자바스크립트에서 함수에 사용된 이름과 일치할 필요가 없습니다.

bigquery.defineFunction(
  '<UDF name>',  // Name used to call the function from SQL

  ['<col1>', '<col2>'],  // Input column names

  // JSON representation of the output schema
  [<output schema>],

  // UDF definition or reference
  <UDF definition or reference>
);

입력 열

입력 열 이름은 입력 테이블 또는 서브 쿼리에 있는 열의 이름(또는 별칭, 해당하는 경우)과 일치해야 합니다.

입력 열이 레코드인 경우, 레코드에서 액세스하려는 리프 필드를 입력 열 목록에서 지정해야 합니다.

예 - 특정 사용자의 이름과 나이가 저장된 레코드가 있는 경우:

person RECORD REPEATED
  name STRING OPTIONAL
  age INTEGER OPTIONAL

이 경우, 이름 및 나이에 대한 입력 지정자는 다음과 같습니다.

['person.name', 'person.age']

이름 또는 나이 없이 ['person']을 사용하면 오류가 발생합니다.

결과 출력은 스키마와 일치합니다. 여기에는 자바스크립트 객체 배열이 표시되고, 각 객체는 'name' 및 'age' 속성을 포함합니다. 예를 들면 다음과 같습니다.

[ {name: 'alice', age: 23}, {name: 'bob', age: 64}, ... ]

출력 스키마

UDF로 생성되며, JSON으로 표현되는 레코드의 스키마 또는 구조를 BigQuery에 제공해야 합니다. 스키마는 중첩 레코드를 포함하여 모든 지원되는 BigQuery 데이터 유형을 포함할 수 있습니다. 지원되는 유형 지정자는 다음과 같습니다.

  • 부울
  • 부동 소수점
  • 정수
  • 레코드
  • 문자열
  • 타임스탬프

다음 코드 예시에서는 출력 스키마에 있는 레코드의 구문을 보여줍니다. 각 출력 필드에는 nametype 속성이 필요합니다. 중첩 필드는 fields 속성도 포함해야 합니다.

[{name: 'foo_bar', type: 'record', fields:
  [{name: 'a', type: 'string'},
   {name: 'b', type: 'integer'},
   {name: 'c', type: 'boolean'}]
}]

각 필드는 다음 값을 지원하는 선택적인 mode 속성을 포함할 수 있습니다.

  • null 사용 가능: 기본값이며 생략 가능합니다.
  • 필수: 지정된 경우 해당 필드를 특정 값으로 설정해야 하며, 정의되지 않은 상태일 수 없습니다.
  • 반복: 지정된 경우 해당 필드가 배열이어야 합니다.

emit() 함수에 전달되는 행은 출력 스키마의 데이터 유형과 일치해야 합니다. 출력 스키마에 제공되었지만 emit 함수에서 생략된 필드는 null로 출력됩니다.

UDF 정의 또는 참조

필요에 따라 bigquery.defineFunction에서 UDF를 인라인으로 정의할 수 있습니다. 예를 들면 다음과 같습니다.

bigquery.defineFunction(
  'urlDecode',  // Name used to call the function from SQL

  ['title', 'num_requests'],  // Input column names

  // JSON representation of the output schema
  [{name: 'title', type: 'string'},
   {name: 'requests', type: 'integer'}],

  // The UDF
  function(row, emit) {
    emit({title: decodeURI(row.title),
          requests: row.num_requests});
  }
);

그렇지 않으면 UDF를 개별적으로 정의하고 bigquery.defineFunction에서 함수에 대한 참조를 전달할 수 있습니다. 예를 들면 다음과 같습니다.

// The UDF
function urlDecode(row, emit) {
  emit({title: decodeURI(row.title),
        requests: row.num_requests});
}

// UDF registration
bigquery.defineFunction(
  'urlDecode',  // Name used to call the function from SQL

  ['title', 'num_requests'],  // Input column names

  // JSON representation of the output schema
  [{name: 'title', type: 'string'},
   {name: 'requests', type: 'integer'}],

  urlDecode  // The function reference
);

오류 처리

UDF 처리 중 예외 또는 오류가 발생하면 전체 쿼리가 실패합니다. 오류는 try-catch 블록을 사용하여 처리할 수 있습니다. 예를 들면 다음과 같습니다.

// The UDF
function urlDecode(row, emit) {
  emit({title: decodeHelper(row.title),
        requests: row.num_requests});
}

// Helper function with error handling
function decodeHelper(s) {
  try {
    return decodeURI(s);
  } catch (ex) {
    return s;
  }
}

// UDF registration
bigquery.defineFunction(
  'urlDecode',  // Name used to call the function from SQL

  ['title', 'num_requests'],  // Input column names

  // JSON representation of the output schema
  [{name: 'title', type: 'string'},
   {name: 'requests', type: 'integer'}],

  urlDecode  // The function reference
);

UDF 및 웹 UI

BigQuery 웹 UI를 사용하면 UDF를 추가하고 쿼리를 실행할 때 이를 사용할 수 있습니다.

기본 요건

BigQuery 웹 UI를 사용하려면 Google Cloud Platform Console에서 BigQuery 지원 프로젝트에 액세스할 수 있는 권한이 사용자 계정에 있어야 합니다.

  1. GCP Console을 이전에 사용한 적이 없으면 Console로 이동하고 서비스 약관에 동의한 후 새 프로젝트를 만듭니다.

  2. BigQuery 웹 UI로 이동합니다.

UDF 추가

  1. 쿼리 작성 버튼을 클릭합니다.

  2. UDF 편집기 탭을 클릭하여 UDF를 추가합니다.

  3. 다음 코드를 복사하여 UDF 편집기 텍스트 영역에 붙여넣습니다.

    // The UDF
    function urlDecode(row, emit) {
      emit({title: decodeHelper(row.title),
            requests: row.num_requests});
    }
    
    // Helper function for error handling
    function decodeHelper(s) {
      try {
        return decodeURI(s);
      } catch (ex) {
        return s;
      }
    }
    
    // UDF registration
    bigquery.defineFunction(
      'urlDecode',  // Name used to call the function from SQL
    
      ['title', 'num_requests'],  // Input column names
    
      // JSON representation of the output schema
      [{name: 'title', type: 'string'},
       {name: 'requests', type: 'integer'}],
    
      urlDecode  // The function reference
    );
    

웹 UI에서 쿼리 실행

  • 쿼리 편집기 탭을 클릭합니다.

  • 다음 쿼리를 복사하여 쿼리 편집기 텍스트 영역에 붙여넣습니다.

    #legacySQL
    SELECT requests, title
    FROM
      urlDecode(
        SELECT
          sum(requests) AS num_requests
        FROM
          [fh-bigquery:wikipedia.pagecounts_201504]
        WHERE language = 'fr'
        GROUP BY title
      )
    WHERE title LIKE '%ç%'
    ORDER BY requests DESC
    LIMIT 100
    

    위 쿼리는 2015년 4월부터 제목에 세디유 문자(ç)가 포함되었으며 방문 빈도가 가장 높은 프랑스어 위키백과 문서를 찾습니다.

  • 쿼리 실행 버튼을 클릭합니다. 쿼리 결과가 버튼 아래에 표시됩니다.

Cloud Storage에서 코드 참조

위 예시에서는 BigQuery 웹 UI에 직접 UDF를 추가했습니다. 또는 자바스크립트 코드의 일부 또는 전체를 Cloud Storage에 저장할 수도 있습니다. UDF 코드가 있는 위치에 관계없이, 코드에서 bigquery.defineFunction 호출을 사용하여 각 UDF를 등록해야 합니다. bigquery.definefunction 호출은 BigQuery 웹 UI 또는 원격 코드 리소스에 제공할 수 있습니다. 원격 소스 파일은 확장자가 '.js'여야 합니다.

예를 들어 제3자 라이브러리, 사용자 고유 UDF 코드, UDF 등록 함수 호출을 개별 파일에서 유지 관리할 수 있습니다. 이러한 파일은 쿼리에서 개별 외부 리소스로 로드됩니다.

BigQuery 웹 UI에서 외부 UDF 참조

  1. 텍스트 영역 아래에서 옵션 표시 버튼을 클릭합니다.

  2. UDF 소스 URI 제목 옆에 있는 편집 버튼을 클릭합니다.

  3. 각 원격 소스 파일에서 UDF 소스 URI 추가 버튼을 클릭하고 Cloud Storage URI를 입력합니다.

    외부 UDF를 사용해서 이전 URL 디코딩 예시를 시도하고 싶으면 URI 필드에 bigquery-sandbox-udf/url_decode.js를 붙여넣습니다. 이러한 단계를 완료한 다음에는 UDF 편집기의 내용이 삭제되었는지 확인해야 합니다.

  4. 확인 버튼을 클릭합니다.

그런 다음 쿼리 편집기 탭으로 이동하고 위 예와 동일한 단계에 따라 쿼리에서 UDF를 사용할 수 있습니다.

일반적으로 외부 UDF를 사용할 때는 웹 UI의 defineFunction 블록 또는 외부 파일에 추가 UDF가 등록되어 있기만 하면 UDF 편집기 텍스트 영역에서 자바스크립트 코드를 추가할 수도 있습니다.

또한 bq 명령줄 도구를 사용하여 Cloud Storage에 저장되어 있는 UDF를 참조할 수도 있습니다. 자세한 내용은 UDF 및 bq 명령줄 도구를 참조하세요.

맨 위로

UDF 및 bq 명령줄 도구

Cloud SDK에서 bq 명령줄 도구를 사용하면 --udf_resource 플래그를 지정하여 UDF가 한 개 이상 포함된 쿼리를 실행할 수 있습니다. 플래그 값은 Cloud Storage(gs://...) URI 또는 로컬 파일의 경로일 수 있습니다. 이 플래그를 반복하여 여러 UDF 리소스 파일을 지정할 수 있습니다.

UDF가 포함된 쿼리를 실행하려면 다음 구문을 사용하세요.

bq query --udf_resource=<file_path_or_URI> <sql_query>

다음 예에서는 로컬 파일에 저장된 UDF를 사용하는 쿼리를 실행하고 마찬가지로 로컬 파일에 저장된 SQL 쿼리를 실행합니다.

UDF 만들기

UDF는 Cloud Storage에 또는 로컬 텍스트 파일로 저장할 수 있습니다. 예를 들어 UDF 및 웹 UI에 설명된 urlDecode UDF를 저장하려면 이름이 urldecode.js인 파일을 만들고 다음 자바스크립트 코드를 파일에 붙여넣은 후 파일을 저장합니다.

// UDF definition
function urlDecode(row, emit) {
  emit({title: decodeHelper(row.title),
        requests: row.num_requests});
}

// Helper function with error handling
function decodeHelper(s) {
  try {
    return decodeURI(s);
  } catch (ex) {
    return s;
  }
}

// UDF registration
bigquery.defineFunction(
  'urlDecode',  // Name used to call the function from SQL

  ['title', 'num_requests'],  // Input column names

  // JSON representation of the output schema
  [{name: 'title', type: 'string'},
   {name: 'requests', type: 'integer'}],

  urlDecode  // The function reference
);

쿼리 만들기

또한 명령줄이 너무 길어지지 않도록 파일에 쿼리를 저장할 수도 있습니다. 예를 들어 이름이 query.sql인 로컬 파일을 만들고 다음 BigQuery 문을 파일에 붙여넣을 수 있습니다.

#legacySQL
SELECT requests, title
FROM
  urlDecode(
    SELECT
      title, sum(requests) AS num_requests
    FROM
      [fh-bigquery:wikipedia.pagecounts_201504]
    WHERE language = 'fr'
    GROUP EACH BY title
  )
WHERE title LIKE '%ç%'
ORDER BY requests DESC
LIMIT 100

파일을 저장한 후에는 명령줄에서 파일을 참조할 수 있습니다.

쿼리 실행

UDF와 쿼리를 개별 파일로 정의한 후에는 명령줄에서 이를 참조할 수 있습니다. 예를 들어 다음 명령어는 사용자가 이름이 query.sql인 파일로 저장한 쿼리를 실행하고 사용자가 만든 UDF를 참조합니다.

$ bq query --udf_resource=urldecode.js "$(cat query.sql)"

UDF 및 BigQuery API

configuration.query

UDF를 사용하는 쿼리에는 쿼리에 사용할 수 있도록 코드 또는 코드 리소스에 대한 위치를 제공하는 userDefinedFunctionResources 요소가 포함되어야 합니다. 제공된 코드에는 쿼리에서 참조되는 모든 UDF에 대한 등록 함수 호출이 포함되어야 합니다.

코드 리소스

쿼리 구성에는 Cloud Storage에 저장된 자바스크립트 소스 파일에 대한 참조 외에도 자바스크립트 코드 BLOB이 포함될 수 있습니다.

인라인 자바스크립트 코드 BLOB은 userDefinedFunctionResource 요소의 inlineCode 섹션에 입력되어 있습니다. 하지만 여러 쿼리 간에 재사용 또는 참조되는 코드는 Cloud Storage에 저장하고 외부 리소스로 참조해야 합니다.

Cloud Storage에서 자바스크립트 소스 파일을 참조하려면 userDefinedFunctionResource 요소의 resourceURI 섹션을 파일의 gs:// URI로 설정합니다.

쿼리 구성에는 여러 userDefinedFunctionResource 요소가 포함될 수 있습니다. 각 요소는 inlineCode 또는 resourceUri 섹션을 포함할 수 있습니다.

다음 JSON 예시는 2개의 UDF 리소스를 참조하는 쿼리 요청을 보여줍니다. 이러한 두 리소스는 인라인 코드의 BLOB 하나와 Cloud Storage에서 읽을 하나의 lib.js 파일입니다. 이 예시에서 myFuncmyFunc에 대한 등록 호출은 lib.js에 의해 제공됩니다.

{
  "configuration": {
    "query": {
      "userDefinedFunctionResources": [
        {
          "inlineCode": "var someCode = 'here';"
        },
        {
          "resourceUri": "gs://some-bucket/js/lib.js"
        }
      ],
      "query": "select a from myFunc(T);"
    }
  }
}

맨 위로

권장사항

UDF 개발

UDF 테스트 도구를 사용하면 BigQuery 비용 부과 없이 UDF를 테스트하고 디버깅할 수 있습니다.

입력 사전 필터링

입력을 UDF로 전달하기 전에 쉽게 필터링할 수 있다면 쿼리 속도가 빨라지고 비용이 낮아질 것입니다.

쿼리 실행 예에서는 전체 테이블 대신 urlDecode에 대한 입력으로 서브 쿼리가 전달됩니다. [fh-bigquery:wikipedia.pagecounts_201504] 테이블에는 약 56억 개의 행이 들어 있습니다. 전체 테이블에서 UDF를 실행한다면 필터링된 서브 쿼리를 사용할 때와 비교해서 자바스크립트 프레임워크가 21배 더 많은 행을 처리해야 합니다.

영구적인 변경 가능 상태 금지

UDF 호출에서는 변경 가능 상태를 저장하거나 액세스하지 않는 것이 좋습니다. 다음 코드 예에서는 이러한 시나리오를 보여줍니다.

// myCode.js
var numRows = 0;

function dontDoThis(r, emit) {
  emit(rowCount: ++numRows);
}

// The query.
SELECT max(rowCount) FROM dontDoThis(t);

BigQuery가 쿼리를 여러 노드 간에 분할하기 때문에 위 예시는 예상한 대로 작동하지 않습니다. 각 노드에는 numRows에 대해 개별 값을 누적하는 독립형 자바스크립트 처리 환경이 포함되어 있습니다.

효율적인 메모리 사용

자바스크립트 처리 환경은 쿼리당 사용 가능한 메모리가 제한되어 있습니다. 로컬 상태를 너무 많이 누적하는 UDF 쿼리는 메모리 소진으로 인해 실패할 수 있습니다.

SELECT 쿼리 확장

UDF에서 선택되는 열을 명시적으로 나열해야 합니다. SELECT * FROM <UDF name>(...)은 지원되지 않습니다.

입력 행 데이터의 구조를 조사하려면 JSON.stringify()를 사용하여 문자열 출력 열을 내보낼 수 있습니다.

bigquery.defineFunction(
  'examineInputFormat',
  ['some', 'input', 'columns'],
  [{name: 'input', type: 'string'}],
  function(r, emit) {
    emit({input: JSON.stringify(r)});
  }
);

맨 위로

한도

  • 단일 행을 처리할 때 UDF가 출력하는 데이터 양은 약 5MB 이하여야 합니다.
  • 각 사용자가 특정 프로젝트에서 동시에 실행할 수 있는 UDF 쿼리 수는 약 6개로 제한됩니다. 동시 쿼리 제한을 초과했다는 오류가 발생하면 몇 분 정도 기다린 후 다시 시도하세요.
  • UDF가 시간 초과되어 쿼리를 완료하지 못할 수 있습니다. 시간 제한은 5분 정도로 짧을 수 있지만, 해당 함수가 소비하는 사용자 CPU 시간과 JS 함수에 대한 입력 및 출력 크기를 포함한 여러 요소에 따라 달라질 수 있습니다.
  • 쿼리 작업이 포함할 수 있는 UDF 리소스 수는 최대 50개입니다(인라인 코드 BLOB 또는 외부 파일).
  • 각 인라인 코드 BLOB 크기는 최대 32KB로 제한됩니다. 더 큰 코드 리소스를 사용하려면 코드를 Cloud Storage에 저장하고 외부 리소스로 참조하세요.
  • 각 외부 코드 리소스 크기는 최대 1MB로 제한됩니다.
  • 모든 외부 코드 리소스의 누적 크기는 최대 5MB로 제한됩니다.

맨 위로

제한사항

  • DOM 객체 Window, Document, Node와 이를 필요로 하는 함수는 지원되지 않습니다.
  • 네이티브 코드를 사용하는 자바스크립트 함수는 지원되지 않습니다.
  • 자바스크립트의 비트 연산은 32비트만 처리합니다.
  • 비확정성으로 인해 사용자 정의 함수를 호출하는 쿼리는 캐시 처리된 결과를 사용할 수 없습니다.

맨 위로

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

도움이 필요하시나요? 지원 페이지를 방문하세요.