홈 / Skills / 개발 생산성 / BigQuery 최적화 BigQuery Optimization
개발 생산성

BigQuery 최적화 BigQuery Optimization

GoogleApache-2.0Skill 언어: 영어
라이선스 확인됨스크립트 없음외부 서비스 필요인증 정보 필요
실행 예시실제 실행 · claude-sonnet-5-5 · 2026년 10월
Optimize this BigQuery query (project shop-prod, I don't know the region): SELECT * FROM shop.orders WHERE EXTRACT(YEAR FROM order_ts) = 2025 AND customer_id NOT IN (SELECT customer_id FROM shop.blocked) ORDER BY order_ts
Skill: bigquery-optimization
Read: references/sql_optimization.md
Read: references/table_partitioning_clustering.md

Scope: project shop-prod, region us. You didn't give a region, so I assumed us. If shop lives elsewhere, use your region in any INFORMATION_SCHEMA queries (for example region-europe-west1). I haven't run anything against your project. This rewrite comes from the query text alone, and I'm assuming order_ts is a TIMESTAMP.

Optimized query

SELECT
  order_id,        -- replace with the columns you actually need
  customer_id,
  order_ts
FROM
  `shop-prod.shop.orders`
WHERE
  order_ts >= TIMESTAMP '2025-01-01 00:00:00'
  AND order_ts <  TIMESTAMP '2026-01-01 00:00:00'
  AND customer_id NOT IN (
    SELECT customer_id
    FROM `shop-prod.shop.blocked`
    WHERE customer_id IS NOT NULL
  )
ORDER BY
  order_ts
LIMIT 1000;  -- remove or adjust if you really need every row

Summary of optimizations

  1. Replaced EXTRACT(YEAR FROM order_ts) = 2025 with a range filter. A function on the column prevents partition pruning and block pruning. A plain range on order_ts lets BigQuery prune statically.
  2. Added WHERE customer_id IS NOT NULL to the NOT IN subquery. This lets BigQuery use an anti-hash join instead of the slower left outer hash join.
    • Behavior change: if shop.blocked contains a NULL customer_id, the original query returns zero rows, because NOT IN with a NULL evaluates to NULL. The rewrite returns the rows you almost certainly intended. If you somehow relied on the old behavior, tell me.
  3. Replaced SELECT * with an explicit column list. This reads less data. I don't know your schema, so fill in the columns you need.
  4. Added LIMIT to the ORDER BY. A full sort of a year of orders is expensive. If you need all rows, drop the LIMIT, or consider whether the sort is needed at all.

Table layout (biggest lever if the table is large)

The range filter only prunes partitions if shop.orders is partitioned on order_ts. To check, run:

bq show --format=prettyjson shop-prod:shop.orders

Look at timePartitioning, clustering and requirePartitionFilter in the output.

If the table is not partitioned, and each day holds roughly 10 GB or more (otherwise cluster only), you can create a partitioned copy:

CREATE TABLE `shop-prod.shop.orders_partitioned`
  PARTITION BY TIMESTAMP_TRUNC(order_ts, DAY)
  CLUSTER BY customer_id
  OPTIONS (require_partition_filter = TRUE)
AS
SELECT * FROM `shop-prod.shop.orders`;

You can't convert the existing table in place. Note that require_partition_filter = TRUE makes any query without an order_ts filter fail.

Validate before running

A dry run shows the bytes the query would process, and it costs nothing:

bq query --use_legacy_sql=false --dry_run "<the optimized query>"

Run it on the original query and on the rewrite, and compare totalBytesProcessed. The dry run won't show savings from clustering, since those happen at execution time.

These changes should improve cost-efficiency, but I can't promise a particular saving until you've compared the dry-run numbers.

Trial with only the Skill and Read tools: no Google Cloud access, no gcloud or bq. So only the SQL-rewrite path was exercised, from the query text alone; the model proposed the bq show and dry-run commands but did not run them. Capacity, billing and gcloud workflows were read but not tried.

하는 일
BigQuery를 최적화하는 Google의 방식을 모델에 전달합니다. SQL을 고쳐 쓰고, 테이블 설계를 제안하고, 요금 선택지를 설명하되, 비용에 영향을 주는 변경은 모델이 직접 실행하지 않고 사용자에게 넘기도록 지시합니다.

동작 방식

  1. SQL 최적화: 구체적인 규칙 목록(EXTRACT 대신 범위 조건, NOT IN 서브쿼리에서 NULL 제외, ORDER BY에 LIMIT, 최상위 SELECT * 피하기, 조인 전 사전 집계 등)과 마지막의 변경 요약.
  2. 테이블 설계: 파티셔닝과 클러스터링 선택, DDL 템플릿, 파티션 없는 테이블 이전, require_partition_filter.
  3. 스토리지와 요금: 논리 스토리지와 물리 스토리지, 타임 트래블, 파티션 만료, 검색 인덱스·구체화된 뷰·BI Engine이 적합한 경우.
  4. 용량: 온디맨드와 Editions에 대한 Slot Estimator 권장 사항을 읽는 법. 약정 구매, 예약 변경, 요금 모델 변경은 Cloud Console에서 사용자가 직접 합니다.

잘 맞는 경우
비싼 쿼리 튜닝, 대형 테이블 설계·수정, BigQuery 청구서 점검.

한계
SQL 재작성은 쿼리 텍스트만으로 가능합니다. 분석 단계는 Google Cloud SDK와 프로젝트 접근 권한이 필요하며, dry run 수치를 직접 확인하는 일을 대신하지는 못합니다.

참고 사항 및 위험

중간 위험: 스크립트는 없지만, 지침에 에이전트가 사용자의 Google Cloud 프로젝트에 `gcloud`와 `bq`를 실행하는 내용이 들어 있습니다. Google Cloud SDK, 로그인한 계정(또는 서비스 계정 키 파일), 결제 계정, IAM 역할(BigQuery 관리자, 데이터 편집자, 작업 사용자 언급)이 필요합니다. 준비 단계에는 프로젝트 설정을 바꾸는 `gcloud config set project`와 `gcloud services enable`, 관련 Skill 설치 명령(`npx skills add ...`)이 포함됩니다. 또한 이 Skill 자신의 `gcloud` 호출에 사용량 귀속 라벨을 붙이고 Google로 보내는 User-Agent에 Skill 이름과 버전을 덧붙이도록 하며, 끄도록 요청할 수 있습니다. 약정 구매, 요금 모델 변경, DDL 실행은 에이전트가 직접 하지 말고 제안만 하여 사용자가 실행하도록 지시합니다. 그래도 각 명령은 승인 전에 꼭 확인하고, 먼저 운영이 아닌 프로젝트에서 시험하며, 쿼리와 스토리지 변경에는 비용이 든다는 점을 기억하세요. 이번 시험 사용에서는 클라우드에 연결하지 않고 SQL 재작성 경로만 사용했습니다.

SKILL.md 원본패키지에 파일 11개