시작하기 전에
- 실습에서는 정해진 기간 동안 Google Cloud 프로젝트와 리소스를 만듭니다.
- 실습에는 시간 제한이 있으며 일시중지 기능이 없습니다. 실습을 종료하면 처음부터 다시 시작해야 합니다.
- 화면 왼쪽 상단에서 실습 시작을 클릭하여 시작합니다.
Create a new dataset and load JSON data into the table
/ 20
Creating arrays with ARRAY_AGG()
/ 20
Querying datasets that already have ARRAYs
/ 20
Explore a dataset with STRUCTs
/ 20
Practice with STRUCTs and ARRAYs
/ 20
BigQuery는 Google의 완전 관리형, 노옵스(NoOps), 저비용 분석 데이터베이스입니다. BigQuery를 사용하면 관리할 인프라나 데이터베이스 관리자가 없어도 테라바이트 단위의 대규모 데이터를 쿼리할 수 있습니다. BigQuery는 SQL을 사용하므로 사용한 만큼만 지불하는 모델의 장점을 활용할 수 있습니다. BigQuery는 데이터를 분석하여 의미 있고 유용한 정보를 찾는 데 집중할 수 있게 해줍니다.
이 실습에서는 BigQuery 내에서 반정형 데이터 작업(JSON, 배열 데이터 유형 수집)을 수행하는 방법을 심도 있게 다룹니다. 중첩되고 반복되는 필드를 포함하는 단일 테이블로 스키마를 비정규화하면 성능이 개선될 수는 있지만 배열 데이터 작업을 위한 SQL 문법이 복잡해질 수 있습니다. 다양한 반정형 데이터 세트의 로드, 쿼리, 문제 해결, 중첩 해제를 연습합니다.
이 실습에서는 다음 사항을 알아봅니다.
각 실습에서는 정해진 기간 동안 새 Google Cloud 프로젝트와 리소스 집합이 무료로 제공됩니다.
시크릿 창을 사용하여 Qwiklabs에 로그인합니다.
실습 사용 가능 시간(예: 1:15:00)을 참고하여 해당 시간 내에 완료합니다.
일시중지 기능은 없습니다. 필요한 경우 다시 시작할 수 있지만 처음부터 시작해야 합니다.
준비가 되면 실습 시작을 클릭합니다.
실습 사용자 인증 정보(사용자 이름 및 비밀번호)를 기록해 두세요. Google Cloud Console에 로그인합니다.
Google Console 열기를 클릭합니다.
다른 계정 사용을 클릭한 다음, 안내 메시지에 이 실습에 대한 사용자 인증 정보를 복사하여 붙여넣습니다.
다른 사용자 인증 정보를 사용하는 경우 오류가 발생하거나 요금이 부과됩니다.
약관에 동의하고 리소스 복구 페이지를 건너뜁니다.
Cloud Console의 BigQuery에 오신 것을 환영합니다라는 메시지 상자가 열립니다. 이 메시지 상자에서는 빠른 시작 가이드 링크 및 UI 업데이트 목록을 확인할 수 있습니다.
새 데이터 세트 이름을 fruit_store로 지정합니다. 다른 옵션(데이터 위치, 기본 테이블 만료)은 기본값으로 유지합니다.
데이터 세트 만들기를 클릭합니다.
일반적으로 SQL에서는 아래에 나와 있는 과일 목록처럼 각 행에 하나의 값만 있습니다.
|
Row |
Fruit |
|
1 |
raspberry |
|
2 |
blackberry |
|
3 |
strawberry |
|
4 |
cherry |
매장에 있는 각 사람별로 과일 항목 목록이 필요하다면 어떻게 해야 할까요? 다음과 같이 표시할 수 있습니다.
|
Row |
Fruit |
Person |
|
1 |
raspberry |
sally |
|
2 |
blackberry |
sally |
|
3 |
strawberry |
sally |
|
4 |
cherry |
sally |
|
5 |
orange |
frederick |
|
6 |
apple |
frederick |
기존의 관계형 데이터베베이스 SQL에서는 반복되는 이름을 보면 즉시 위와 같은 테이블을 두 개의 개별 테이블, 즉 '과일 항목'과 '사람'으로 분할해야겠다고 생각할 것입니다. 이 프로세스를 정규화(단일 테이블에서 다수의 테이블로 변환)라고 합니다. 이는 mySQL과 같은 트랜잭션 데이터베이스에서 일반적으로 활용하는 방법입니다.
데이터 웨어하우징의 경우에는 데이터 분석가가 이와 반대로 진행(비정규화)하고 많은 개별 테이블을 하나의 대형 보고 테이블로 변환합니다.
이제 반복되는 필드를 사용하여 단일 테이블에서 다양한 수준의 세분화된 데이터를 저장하는 다른 방법을 알아보겠습니다.
|
Row |
Fruit(배열) |
Person |
|
1 |
raspberry |
sally |
|
blackberry | ||
|
strawberry | ||
|
cherry | ||
|
2 |
orange |
frederick |
|
apple |
이 테이블의 이상한 점은 무엇인가요?
핵심은 무엇일까요? 바로 array 데이터 유형이라는 것입니다.
과일 배열을 더 쉽게 해석해 보면 다음과 같습니다.
|
Row |
Fruit(배열) |
Person |
|
1 |
[raspberry, blackberry, strawberry, cherry] |
sally |
|
2 |
[orange, apple] |
frederick |
이 두 테이블은 동일한 테이블입니다. 여기에서 배울 수 있는 두 가지 요점은 다음과 같습니다.
직접 해 보세요.
실행을 클릭합니다.
이제 다음을 실행해 봅니다.
다음과 같은 오류가 반환될 것입니다.
Error: Array elements of types {INT64, STRING} do not have a common supertype at [3:1]
배열은 한 가지 데이터 유형(전부 문자열, 전부 숫자)만 사용할 수 있습니다.
실행을 클릭합니다.
결과를 보고 JSON 탭을 클릭하여 결과의 중첩된 구조를 확인합니다.
BigQuery로 수집해야 하는 JSON 파일이 있다면 어떻게 해야 할까요?
fruit_store 데이터 세트에 새 테이블을 만듭니다.cloud-training/data-insights-course/labs/optimizing-for-performance/shopping_cart.json
새 테이블 이름을 fruit_details로 정합니다.
테이블 만들기를 클릭합니다.
스키마에서 fruit_array는 REPEATED로 표시되며 이는 배열이라는 뜻입니다.
요약
내 진행 상황 확인하기를 클릭하여 목표를 확인합니다.
아직 테이블에 배열이 없다면 배열을 만들어 보세요.
ARRAY_AGG() 함수를 사용하여 문자열 값을 배열로 집계해 보겠습니다. 아래 쿼리를 복사하고 붙여넣기하고 해당 공개 데이터 세트를 살펴보세요.ARRAY_LENGTH() 함수를 사용하여 조회된 페이지 및 제품 수를 계산해 보겠습니다.ARRAY_AGG()에 DISTINCT를 추가합니다.요약
배열로 다음과 같은 유용한 작업을 수행할 수 있습니다.
ARRAY_LENGTH(<array>)로 요소 수 찾기ARRAY_AGG(DISTINCT <field>)로 중복된 요소 삭제ARRAY_AGG(<field> ORDER BY <field>)로 요소 정렬ARRAY_AGG(<field> LIMIT 5)로 제한내 진행 상황 확인하기를 클릭하여 목표를 확인합니다.
Google 애널리틱스의 BigQuery 공개 데이터 세트 bigquery-public-data.google_analytics_sample에는 본 과정의 데이터 세트 data-to-insights.ecommerce.all_sessions보다 필드와 행이 더 많습니다. 게다가 기본적으로 제품, 페이지, 거래와 같은 필드 값이 ARRAY로 저장되어 있습니다.
쿼리를 실행합니다.
결과에서 오른쪽으로 스크롤하여 hits.product.v2ProductName 필드로 이동합니다(여러 필드 별칭은 곧 설명해 드리겠습니다).
다음 오류가 반환됩니다.
Cannot access field page on a value with type ARRAY<STRUCT<hitNumber INT64, time INT64, hour INT64, ...>> at [3:8]
일반적으로 REPEATED 필드(배열)를 쿼리하려면 먼저 배열을 다시 행으로 나누어야 합니다.
예를 들어 hits.page.pageTitle의 배열이 현재 다음과 같이 단일 행으로 저장되어 있습니다.
이를 다음과 같이 변경해야 합니다.
SQL로 어떻게 작업하면 될까요?
나중에 UNNEST() 함수를 좀 더 자세하게 설명하겠지만 지금은 다음 사항만 기억하세요.
내 진행 상황 확인하기를 클릭하여 목표를 확인합니다.
필드 별칭 hit.page.pageTitle이 마침표로 구분된 3개의 필드처럼 보이는 이유가 궁금하셨을 것입니다. ARRAY 값을 사용하면 필드 단위로 깊이 있게 분석할 수 있는 것처럼, 관련 필드를 그룹화하여 스키마에서 폭넓게 분석할 수 있는 데이터 유형도 있습니다. 해당 SQL 데이터 유형은 STRUCT 데이터 유형입니다.
STRUCT의 개념을 이해하는 가장 쉬운 방법은 기본 테이블에 개별 테이블이 사전에 조인되어 있다고 생각하는 것입니다.
STRUCT에는 다음 항목이 있을 수 있습니다.
테이블과 비슷한 것 같나요?
탐색기 창으로 전환한 후 + 데이터 추가 > 이름으로 프로젝트에 별표표시를 클릭합니다.
bigquery-public-data라고 입력하고 별표표시를 클릭합니다.
고정된 프로젝트 목록에서 bigquery-public-data를 클릭하여 펼칩니다.
데이터 세트를 클릭하고 google_analytics_sample을 찾아 엽니다.
ga_sessions 테이블을 클릭합니다.
대형 보고 테이블을 STRUCT(사전 조인된 '테이블') 및 ARRAY(심층 세분화)로 저장하면 다음을 수행할 수 있습니다.
내 진행 상황 확인하기를 클릭하여 목표를 확인합니다.
다음 데이터 세트는 트랙을 도는 주자들의 랩 시간입니다. 각 랩은 '스플릿'이라고 지칭합니다.
|
Row |
runner.name |
runner.split |
|
1 |
Rudisha |
23.4 |
필드 별칭과 관련해 무엇을 파악하셨나요? 구조체 내에 중첩된 필드(이름 및 스플릿은 주자의 하위 집합)가 있으므로 점 표기법으로 표시됩니다.
단일 경주에서 한 주자의 스플릿 타임(랩당 시간)이 여러 개라면 어떻게 해야 할까요?
|
Row |
runner.name |
runner.splits |
|
1 |
Rudisha |
23.4 |
|
26.3 | ||
|
26.4 | ||
|
26.1 |
요약하면 다음과 같습니다.
기존 탐색기 창으로 전환하고 racing이라는 제목의 새 데이터 세트를 만듭니다.
race_results라는 제목의 새로운 테이블을 만듭니다.
다음 Google Cloud Storage JSON 파일을 수집합니다.
cloud-training/data-insights-course/labs/optimizing-for-performance/race_results.json
테이블 만들기를 클릭합니다.
로드가 완료되면 새로 만든 테이블의 스키마를 미리보기합니다.
어느 필드가 STRUCT인가요? 어떻게 아셨나요?
RECORD 유형이므로 participants 필드가 STRUCT입니다.
어느 필드가 ARRAY인가요?
participants.splits 필드가 상위 participants 구조체 내부에 있는 float 배열입니다. 이 필드의 모드는 배열을 나타내는 REPEATED입니다. 단일 필드 내에 여러 값이 있으므로 해당 배열의 값은 중첩된 값이라고 합니다.
몇 개의 행이 반환되었나요?
정답: 1
각 주자 이름 및 경주 유형을 나열하려면 어떻게 해야 하나요?
Error: Cannot access field name on a value with type ARRAY<STRUCT<name STRING, splits ARRAY<FLOAT64>>> at [2:27]
집계 함수를 사용할 때 GROUP BY를 사용하는 것을 깜빡한 것과 마찬가지로, 여기서는 2개의 다른 세분화 수준이 있습니다. 경주의 행은 1개이고 참가자 이름의 행은 3개입니다. 그렇다면 다음과 같은 형태를…
|
Row |
race |
participants.name |
|
1 |
800M |
Rudisha |
|
2 |
??? |
Makhloufi |
|
3 |
??? |
Murphy |
다음과 같이 바꾸려면 어떻게 해야 할까요?
|
Row |
race |
participants.name |
|
1 |
800M |
Rudisha |
|
2 |
800M |
Makhloufi |
|
3 |
800M |
Murphy |
기존의 관계형 SQL에서는 경주 테이블과 참가자 테이블이 있는 경우 두 테이블에서 정보를 얻기 위해 무엇을 수행해야 했나요? 둘을 JOIN했을 것입니다. 여기서 참가자 STRUCT(개념적으로 테이블과 매우 유사함)는 이미 경주 테이블에 속해 있지만 STRUCT가 아닌 필드 'race'와의 상관관계가 아직 올바르게 규정되지는 않았습니다.
800M 경주와 첫 번째 테이블의 각 주자의 상관관계를 보여주기 위해 사용할 SQL 명령어 두 단어는 무엇인가요?
정답: CROSS JOIN
좋습니다.
Error: Table "participants" must be qualified with a dataset (e.g. dataset.table).
참가자 STRUCT가 테이블과 유사하더라도 엄밀히 말하자면 여전히 racing.race_results 테이블의 필드입니다.
그 결과 경주별로 모든 주자를 나열했습니다.
|
Row |
race |
name |
|
1 |
800M |
Rudisha |
|
2 |
800M |
Makhloufi |
|
3 |
800M |
Murphy |
|
4 |
800M |
Bosse |
|
5 |
800M |
Rotich |
|
6 |
800M |
Lewandowski |
|
7 |
800M |
Kipketer |
|
8 |
800M |
Berian |
다음을 통해 마지막 쿼리를 단순화할 수 있습니다.
이렇게 하면 동일한 쿼리 결과가 표시됩니다.
경주 유형이 하나 이상인 경우(800M, 100M, 200M) CROSS JOIN은 카티전 프로덕트처럼 모든 주자의 이름을 가능한 모든 경주에 연결하지 않을까요?
정답: 아닙니다. 이 교차 조인은 단일 행과 연결된 요소만 압축해제하는 상관 교차 조인입니다. 자세한 설명은 ARRAY 및 STRUCT 다루기를 참조하세요.
내 진행 상황 확인하기를 클릭하여 목표를 확인합니다.
STRUCT 요약:
STRUCT("Rudisha" as name, [23.4, 26.3, 26.4, 26.1] as splits) AS runner
앞선 만든 racing.race_results 테이블을 사용하여 아래 질문에 답변해 보세요.
과제:전체 주자 수에 대한 COUNT 쿼리 작성하기
예시 답안:
|
Row |
racer_count |
|
1 |
8 |
정답: 경주에 참가한 주자는 8명입니다.
이름이 R로 시작하는 주자의 총 경주 시간을 나열하는 쿼리를 작성해 봅니다. 전체 시간이 빠른 순서대로 결과를 정렬하세요. UNNEST() 연산자를 사용하고 일부만 작성된 다음 쿼리를 사용해 시작합니다.
예시 답안:
|
Row |
name |
total_race_time |
|
1 |
Rudisha |
102.19999999999999 |
|
2 |
Rotich |
103.6 |
800M 경주에서 기록된 가장 빠른 랩 시간이 23.2초인 것을 확인했지만 해당 랩을 뛴 주자를 확인하지는 못했습니다. 해당 결과를 반환하는 쿼리를 만들어 보세요.
예시 답안:
|
Row |
name |
split_time |
|
1 |
Kipketer |
23.2 |
지금까지 유용한 정보를 얻기 위해 JSON 데이터 세트를 수집하고, ARRAY 및 STRUCT를 만들고, 반정형 데이터의 중첩을 해제해 보았습니다.
추가 자료는 배열 다루기를 참조하세요.
Copyright 2026 Google LLC All rights reserved. Google 및 Google 로고는 Google LLC의 상표입니다. 기타 모든 회사명 및 제품명은 해당 업체의 상표일 수 있습니다.
현재 이 콘텐츠를 이용할 수 없습니다
이용할 수 있게 되면 이메일로 알려드리겠습니다.
감사합니다
이용할 수 있게 되면 이메일로 알려드리겠습니다.
한 번에 실습 1개만 가능
모든 기존 실습을 종료하고 이 실습을 시작할지 확인하세요.