본문 바로가기
GCP/BIGQUERY

[BigQuery] ARRAY, NESTED

by 오늘은강박사갈거야~~ 2022. 4. 3.
반응형

- 이 글은 제가 공부를 하며, 이해한 것을 바탕으로 작성하는 글입니다.

- 그렇기에 틀리거나 잘못된 부분이 있을 수 있습니다.

- 글의 오류를 발견하시면, 댓글로 말씀해 주시면 정말 감사하겠습니다.

- 이 글은 구글의 교육 ARRAY 페이지(GSP416)DOCS를 정리한 것입니다. 


일반적으로 아래와 같은 테이블들이 존재할 때, 우리는 불편하다. 뭔지 모를 쪼개야 할 것 같은 생각이 들기 때문이다. 정규화를 해야 하겠군과 같은 생각이 든다. 

사람 좋아하는 과일
강박사 사과
강박사 수박
줄줄이 사과
줄줄이 바나나
용용이 레몬
용용이 사과

 

그러나 BIGQUERY에서는 RDB가 아니기에 정규화를 해야하겠다는 생각을 버려야 한다. 오히려 반정 규화를 해서 테이블을 합쳐 놓는 것이 성능이 가장 좋다. 

 

BIGQUERY에서는 ARRAY를 활용하여 아래와 같이 처리할 수 있다. 마치 테이블 안에 테이블이 있는 것과 유사하다. 

테이블 내부의 한 컬럼에 다른 테이블이 이미 조인되어 하나의 테이블이 되어있다는 식으로 이해하면 될 것이다. 

  SELECT 
      ["사과", "수박"] AS 좋아하는 과일, "강박사" AS 사람
  UNION ALL
  SELECT 
      ["사과", "바나나"] AS 좋아하는 과일, "줄줄이" AS 사람
  UNION ALL
  SELECT 
      ["레몬", "사과"] AS 좋아하는 과일, "용용이" AS 사람​

NESTED, 즉 중첩 필드(칼럼)이라는 것으로 처리한다. JSON 내부를 배열로 처리하는 것과 동일하다고 볼 수 있다. 

이 떄 주의할 점은 배열 내부의 모든 값은 동일한 타입이 들어간다는 것이다. 

 

 

 

이러한 ARRAY를 생성하는 방법은 아래와 같다. 자주 사용하는 것들만 정리하였다. 더 많은 내용은 공식 DOCS에 들어가서 확인하자.

ARRAY 칼럼 생성 방법

1. 대괄호

SELECT [A,B,C,D, . . .] AS column_name
SELECT ARRAY
  (SELECT 1 UNION ALL
   SELECT 2 UNION ALL
   SELECT 3) AS ARRAY_컬럼;


결과 
+-----------+
| ARRAY_컬럼 |
+-----------+
| [1, 2, 3] |
+-----------+

 

2. ARRAY(TYPE)

SELECT ARRAY<STRING>[A,B,C,D, . . .] AS column_name

 

3.GENERATE_ARRAY

SELECT GENERATE_ARRAY(시작하는 숫자, 종료되는 숫자, 간격) AS column_name

- 간격 parameter의 default 값은 1이다.

- 이 함수에서는 숫자의 간격에 따른 증감으로 ARRAY 내부의 값을 표현하므로, 데이터의 유형이 아래와 같이 한정된다. 

  • INT64
  • NUMERIC
  • BIGNUMERIC
  • FLOAT64
SELECT GENERATE_ARRAY(10, 0, -3) AS ARRAY_컬럼명;

+---------------+
| ARRAY_컬럼명 |
+---------------+
| [10, 7, 4, 1] |
+---------------+

-  시작 숫자가 종료 숫자보다 작거나, 동일하게 설정한 것과 같은 이상 상황에는 빈 array를 반환한다. 

SELECT GENERATE_ARRAY(10, 0, 3) AS ARRAY_컬럼명;

+---------------+
| ARRAY_컬럼명  |
+---------------+
| []            |
+---------------+

 

ARRAY 컬럼이 없는 테이블에서 ARRAY 컬럼 생성 방법

ARRAY_AGG()

다음은 기존 테이블에 ARRAY 컬럼이없을 때, 생성하려는 경우이다. 함수는 ARRAY_AGG()를 사용한다.

아래 테이블을 보자. 이 테이블은 ARRAY 칼럼이 없는 일반 테이블의 일부를 가져온 것이다.

여기 테이블에서 v2ProductName, pageTitle칼럼을 ARRAY 칼럼으로 변경해 보자.

SELECT
  fullVisitorId,
  date,
  ARRAY_AGG(v2ProductName) AS products_viewed,
  ARRAY_AGG(pageTitle) AS pages_viewed
FROM 테이블명
ORDER BY date

아래와 같은 결과가 도출 된다. 

 

 

ARRAY_LENGTH(ARRAY 칼럼명)

이렇게 ARRAY로 데이터를 정리하면, 배열 내부 개수를 알아야 할 때가 있다. 이 때는 ARRAY_LENGTH()를 이용한다.

위와 동일한 query에 아래와 같이 추가해준다. 

SELECT
  fullVisitorId,
  date,
  ARRAY_AGG(v2ProductName) AS products_viewed,
  ARRAY_LENGTH(ARRAY_AGG(v2ProductName)) AS num_products_viewed,
  ARRAY_AGG(pageTitle) AS pages_viewed
  ARRAY_LENGTH(ARRAY_AGG(pageTitle)) AS num_pages_viewed
FROM 테이블명
ORDER BY date

 

 

ARRAY_AGG(DISTINCT 칼럼명)

개수를 세다보면, 고유 값의 개수가 필요할 때가 존재한다. 이럴 때에는 아래와 같이 DISTINCT를 사용하면 된다.

SELECT
  fullVisitorId,
  date,
  ARRAY_AGG(DISTINCT v2ProductName) AS products_viewed,
  ARRAY_LENGTH(ARRAY_AGG(DISTINCT v2ProductName)) AS distinct_products_viewed,
  ARRAY_AGG(DISTINCT pageTitle) AS pages_viewed,
  ARRAY_LENGTH(ARRAY_AGG(DISTINCT pageTitle)) AS distinct_pages_viewed
FROM 테이블명
ORDER BY date

 

ARRAY 칼럼이 있는 테이블에서 QUERY 하는 방법

그럼 ARRAY 칼럼에 쿼리를 날릴 때에는 어떻게 해야 하는가?  예를 들어 보자. 

이 테이블은 Totals라는 칼럼이 ARRAY로 되어있고 그 내부에 또 다른 ARRAY가 존재한다. 여기서 내부의 값을 확인하려면 어떻게 해야 할까? 일반적으로 BIGQUERY에서는 어떤 값의 하위 범주를 표현할 때, 마침표(.)를 사용하는데 이와 같이 처리하면 될까?

SELECT
  totals.pageviews
FROM 테이블 명

잘 된다. 

그러나, 중첩이 여러 번 되어있는 칼럼을 쿼리하려면 이와 다른 방법이 필요하다. 

아래 예시를 보자. 아래는 ARRAY 컬럼 내부에 또 ARRAY 칼럼이 존재하는 경우이다. 

아래와 같은 쿼리를 날리면, 에러가 나온다. 

SELECT
  hits.page.pageTitle
FROM 테이블 명
Cannot access field page on a value with type

이 에러는 BIGQUERY를 사용하면서 상당히 많이 보게 될 에러 문이다. 이는 배열로 되어있는 것을 배열을 없애는 작업 후에, 쿼리를 처리해야 한다.

UNNEST() 함수 사용

사용 방법은 간단하다. ARRAY로 되어있는 칼럼을 기존 컬럼으로 바꾸어주면 된다. 이 떄, 해당 컬럼을 UNNEST해주면 된다. 그런데 뭔가 특이하다. 마치 하나의 테이블 취급을 해주듯, 기존의 테이블 명 뒤에 , 를 하고 붙이다니.

이는 ARRAY 컬럼을 처음 설명할 때 언급했던, 테이블 속의 테이블이라는 개념을 생각하면 될 것이다. 

SELECT DISTINCT
  h.page.pageTitle
FROM 테이블명, UNNEST(hits) AS h

쿼리 결과는 아래와 같이 잘 나오게 된다.

 

반응형

댓글