本文へ移動
cccskills
無料GitHub で公開日本語紹介

dbt

dbtのモデル構成やBigQuery向けSQLの書き方、列名・型・結合の注意点を参照し、分析用テーブルやクエリを作成・検証するスキルです。

原文原文の説明を見る

dbt (data build tool) project structure, SQL patterns, and best practices for the analytics warehouse. Use this skill when working with dbt models, testing SQL queries, or creating new analytical tables.

BuilderIO/agent-nativeスター 7,519(GitHub 7,519 + cccskills 0)
連携ツールHubSpotGoogle Cloud

実行スコア 未検証

インストール方法を見る

こんなときに便利

  • 分析用のdbtモデルを追加したいとき
  • BigQuery SQLをモデル化前に検証したいとき
  • 商談やフォームのデータを結合したいとき
  • 列名や型の不一致を調べたいとき

日本語での紹介

できること

分析用データウェアハウスでdbtモデルやSQLを作る際の構成と注意点をまとめています。staging、intermediate、mart、analyticsの各層の役割や、テーブル・ビュー・incrementalなどのモデル設定を確認できます。BigQueryでよく起きる列名の食い違い、文字列から数値や真偽値への型変換、重複除外に使うQUALIFY、NULLを含む結合、ARRAY_AGGの構文上の注意も扱います。モデル作成前にクエリをテストする手順や、HubSpotの商談・コンタクト・フォーム、訪問者・サインアップ・契約などの代表的な結合例、処理量を抑えるための絞り込み方も参照できます。

こんなときに便利

新しい分析テーブルを追加する人、dbtモデルのSQLを検証したい人に向いています。既存モデルの列や結合キーを調べるとき、集計結果の重複や型不一致、クエリの処理量が気になる場合にも役立ちます。

使い方の例

  • 「商談に関連するフォーム送信を結合するSQLを作ってください」
  • 「このBigQueryクエリをモデル化する前にテストする手順を教えてください」
  • 「dbtモデルで列名と型の不一致が起きる原因を確認してください」

注意点

資料ではdbt/への書き込みが制限されており、SQLをcode/.builder/dbt-models/で作成・検証した後、配置は利用者が手動で行う手順です。SQLの検証にはcode/scripts/のスクリプトとBigQueryの実行環境を使います。

この紹介文は、公開されている SKILL.md をもとに AI(OpenAI Codex)が作成しました。正確な仕様は下の原文を確認してください。

含まれるファイル(1)

  • SKILL.md10.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

dbt Integration

Project Location

  • Root: dbt/ (in workspace root, NOT in code/)
  • Models: dbt/models/ organized by layer (staging, intermediate, mart, analytics)
  • Config: dbt/dbt_project.yml
  • Profiles: dbt/profiles.yml

Access Control

⚠️ CRITICAL: The dbt directory has restricted write access. When creating or modifying dbt models:

  1. Create SQL in code/.builder/dbt-models/ first
  2. Test the query using a script in code/scripts/
  3. Validate results before requesting deployment
  4. User must manually copy to dbt/models/ directory

Schema Organization

SchemaPurposeExamples
dbt_staging_bigqueryRaw staged events from BigQueryfirst_pageviews, all_pageviews, signups
dbt_stagingRaw staged data from other sourceshubspot_companies, hubspot_contacts
dbt_intermediateJoins, transforms, denormalizationhubspot_form_submissions, deal_first_contact
dbt_mappingJoin tables, ID mappingshs_deals_to_contact_id, user_id_to_org_id
dbt_martDimensional models (fact/dim tables)dim_hs_deals, dim_hs_contacts, dim_subscriptions
dbt_analyticsReporting views, aggregatesdeals_by_motion, revenue_funnel, active_users
dbt_devDevelopment/testing (EXCLUDE from queries)Auto-filtered by BigQuery lib

Model Configuration Best Practices

Standard config block

{{
    config(
        schema="dbt_analytics",          -- Target schema
        materialized="table",             -- or "view", "incremental"
        tags=["daily", "analytics", "hubspot"],  -- For orchestration/docs
    )
}}

Common materializations

  • table - Full refresh daily, good for < 10M rows
  • view - No storage, always fresh, good for simple transforms
  • incremental - Append-only, for large event tables

SQL Patterns & Gotchas

1. Column Name Mismatches

⚠️ Common bug source: Column names differ between spec and actual tables

Spec ColumnActual ColumnTable
first_pageview_datecreated_date (TIMESTAMP)first_pageviews
channelfirst_touch_channelall_pageviews
referrerc_referrerall_pageviews
user_create_dateuser_create_dproduct_signups
deal_stagestage_namedim_hs_deals
deal_amountamountdim_hs_deals

Always verify column names by querying INFORMATION_SCHEMA.COLUMNS or reading the source dbt model.

2. ARRAY_AGG Syntax

❌ WRONG (DISTINCT + ORDER BY non-argument):

ARRAY_AGG(DISTINCT form_name IGNORE NULLS ORDER BY form_fill_date LIMIT 1)

✅ CORRECT (remove DISTINCT or order by same column):

-- Option 1: Remove DISTINCT (ORDER BY creates uniqueness)
ARRAY_AGG(form_name IGNORE NULLS ORDER BY form_fill_date LIMIT 1)[SAFE_OFFSET(0)]

-- Option 2: Order by the aggregated column
ARRAY_AGG(DISTINCT form_name ORDER BY form_name LIMIT 1)[SAFE_OFFSET(0)]

3. Type Casting

BigQuery dbt models store booleans as strings in some tables. Always cast:

-- dim_hs_deals.is_closed_won is STRING 'true'/'false', not BOOL
CASE WHEN CAST(is_closed_won AS STRING) = 'true' THEN 1 ELSE 0 END

-- Amounts may be STRING, cast to numeric
SUM(CAST(amount AS FLOAT64))

4. Email Matching

Always use case-insensitive email matching:

LOWER(qf.email) = LOWER(c.email)

5. QUALIFY for Deduplication

Use QUALIFY for window function filtering (cleaner than subquery):

SELECT *
FROM table
QUALIFY ROW_NUMBER() OVER (PARTITION BY deal_id ORDER BY created_date) = 1

6. NULL-Safe Joins

When joining on potentially NULL columns (like visitor IDs):

LEFT JOIN forms f
  ON (
    LOWER(f.email) = LOWER(c.email)
    OR (f.b_visitor_id IS NOT NULL AND f.b_visitor_id = c.b_visitor_id)
  )

Common Join Paths

HubSpot Deals → Contacts → Forms

FROM {{ ref("dim_hs_deals") }} d
LEFT JOIN {{ ref("hs_deals_to_contact_id") }} dc
  ON d.deal_id = dc.deal_id
LEFT JOIN {{ ref("dim_hs_contacts") }} c
  ON dc.contact_id = c.contact_id
LEFT JOIN {{ ref("hubspot_form_submissions") }} f
  ON LOWER(f.email) = LOWER(c.email)
  AND f.form_fill_date < d.createdate

Key points:

  • hs_deals_to_contact_id unnests the associatedcontactids JSON array
  • Multiple contacts per deal → need aggregation or QUALIFY to dedupe
  • Match contacts to forms by email AND/OR b_visitor_id
  • Timestamp filter (form_fill_date < deal.createdate) for attribution

Visitor → Signup → Subscription

FROM {{ ref("first_pageviews") }} fp
LEFT JOIN {{ ref("signups") }} s
  ON fp.visitor_id = s.visitor_id
LEFT JOIN {{ ref("dim_subscriptions") }} sub
  ON s.root_organization_id = sub.root_id

Contact → User → Organization

-- Use product_signups for user data
FROM {{ ref("dim_hs_contacts") }} c
LEFT JOIN {{ ref("product_signups") }} ps
  ON LOWER(ps.email) = LOWER(c.email)
  OR (ps.user_id IS NOT NULL AND ps.user_id = c.builder_user_id)
LEFT JOIN {{ ref("dim_root_organizations") }} ro
  ON ps.user_id = ro.user_id -- or use appropriate join key
WHERE ps.user_create_d IS NOT NULL

Important: Use dbt_analytics.product_signups for signup data - it has the most complete user coverage. Match on both email and user_id for best results.

Testing Queries Before Creating Models

Always test SQL before creating dbt model:

  1. Create test script in code/scripts/test-<feature>.sql
  2. Write BigQuery SQL with fully qualified table names:
    FROM `your-project-id.dbt_mart.dim_hs_deals`
    
  3. Create runner script in code/scripts/test-<feature>.ts:
    import { runQuery } from "../server/lib/bigquery";
    import { readFileSync } from "fs";
    const sql = readFileSync("scripts/test-<feature>.sql", "utf-8");
    const result = await runQuery(sql);
    console.log(result.rows);
    
  4. Run: pnpm action test-<feature>
  5. Iterate until results are correct
  6. Convert to dbt syntax (replace table names with {{ ref("table") }})
  7. Save final SQL to code/.builder/dbt-models/<model_name>.sql

Deal Motion Classification Patterns

Warm Outbound Detection

To detect if a contact had a product signup before deal creation, use dbt_analytics.product_signups:

-- Join to product_signups (match by email OR user_id)
-- AND signup was BEFORE deal creation
LEFT JOIN {{ ref("product_signups") }} ps
  ON (
    LOWER(ps.email) = LOWER(c.email)
    OR (ps.user_id IS NOT NULL AND ps.user_id = c.builder_user_id)
  )
  AND ps.user_create_d < d.createdate

Key columns in product_signups:

  • user_id - Builder user ID
  • email - User email
  • user_create_d (TIMESTAMP) - Signup/user creation date

Critical: Match on both email AND user_id with OR logic for complete coverage. user_create_d is already TIMESTAMP, no conversion needed.

Do NOT use:

  • dbt_staging_bigquery.signups - incomplete coverage
  • dim_hs_contacts.sign_up_time_stamp - DATE type, requires conversion and has gaps

Form Submission Attribution

When attributing form submissions to deals/contacts:

Qualifying form categories (based on actual data analysis):

WHERE (
  -- Sales-related forms
  LOWER(form_name) LIKE '%sales%'
  OR LOWER(conversion_details) LIKE '%sales%'

  -- Demo forms
  OR LOWER(form_name) LIKE '%demo%'
  OR LOWER(conversion_details) LIKE '%demo%'

  -- Specific high-intent forms
  OR form_name = '[Marketing]  | Component Indexing Request'
  OR conversion_details = 'Unlock Ent Trial'
)

Common form names (March 2026 data):

  • [Marketing] Sales Demo Form | 7.20.23 - 5,803 submissions
  • [Marketing] Sales Demo Form - Unlock Enterprise Features - 3,971 submissions
  • Demo Library Form - 1,866 submissions
  • [Marketing] | Component Indexing Request - 51 submissions

Model Documentation

Add to dbt/models/analytics/_models.yml:

- name: deals_inbound_outbound_motion
  description: >
    Classifies Enterprise deals as Inbound or Outbound based on whether 
    any associated contact filled a qualifying form before deal creation.
  columns:
    - name: deal_id
      description: Unique deal identifier
    - name: deal_motion
      description: "Inbound or Outbound classification"
    - name: qualifying_form_count
      description: "Number of distinct qualifying forms filled by associated contacts"
    - name: first_qualifying_form_name
      description: "Name of earliest qualifying form"

Performance Considerations

  • Byte limits: BigQuery queries have 750GB byte limit
  • Table size: dim_hs_deals ~3,400 rows, hubspot_form_submissions ~20K rows
  • Caching: 24-hour cache in server/lib/bigquery.ts
  • Enterprise filter: Always filter to Enterprise pipelines early in WHERE clause
  • Avoid: Unnecessary JOINs, avoid SELECT * from large tables

Useful AI Instructions to Add

Based on this analysis, here are additional AI instructions that would be helpful:

  1. Form submission attribution logic - Document the exact form categories that qualify as "inbound"
  2. Deal-to-contact join patterns - The unnesting of associatedcontactids is non-obvious
  3. Column name mapping reference - Centralized list of common mismatches
  4. Type casting patterns - Which fields need CAST and to what type
  5. Email matching best practices - Always case-insensitive, NULL-safe for visitor IDs
  6. ARRAY_AGG syntax rules - DISTINCT + ORDER BY gotcha
  7. QUALIFY usage - Preferred over subquery for window function filtering
  8. Enterprise pipeline filter - Standard WHERE clause for enterprise deals

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

a2a-assets

無料概要あり

複数のアプリやMCPホストからブランド画像・動画を生成、調整、書き出し、挿入する作業を支援します。ライブラリ選択や修正依頼も扱います。

  • スライド用のブランド画像を作りたいとき
  • 既存素材をフィードバックに合わせて直したいとき
  • 動画素材を生成して書き出したいとき
BuilderIO/agent-native7,5192026年10月26日 更新

account-health

無料概要あり

特定の顧客企業について、契約と製品利用の数値を照らし合わせ、根拠や不足情報を明記した健全性レポートをまとめます。

  • 顧客の健全性を確認したいとき
  • 契約席数と実利用を比べたいとき
  • 更新前に利用状況を整理したいとき
BuilderIO/agent-native7,5192026年10月26日 更新

actions

無料概要あり

アプリの操作をエージェントと画面の両方から呼び出せるアクションとして実装し、API連携や画面のデータ取得を一貫した形で追加するスキルです。

  • 新しいアプリ操作を追加したいとき
  • 外部APIと連携したいとき
  • 画面からデータを取得したいとき
BuilderIO/agent-native7,5192026年10月26日 更新

adding-a-feature

無料概要あり

新機能をUI・エージェント用アクション・説明資料・画面状態の4領域でそろえ、利用者とエージェントのどちらからも一貫して使えるようにするチェックリストです。

  • 新機能追加の作業範囲を整理したいとき
  • UIとエージェント操作をそろえたいとき
  • 画面状態の同期漏れを防ぎたいとき
BuilderIO/agent-native7,5192026年10月26日 更新

adhoc-analysis

無料概要あり

複数のデータソースを照合して、傾向や指標、根拠をまとめた分析結果を作成します。必要に応じて、再実行できるダッシュボードとして保存できます。

  • 商談と通話記録を照合したいとき
  • 顧客の利用状況を横断して調べたいとき
  • 繰り返し更新できる分析を保存したいとき
BuilderIO/agent-native7,5192026年10月26日 更新

admin-surfaces

無料概要あり

Analyticsの管理画面で、機能フラグの状態確認と切り替え、ダッシュボード利用状況の調査、接続先アプリのデータベース管理を行います。

  • アプリごとの機能フラグを確認・変更したいとき
  • ダッシュボードの所有者や利用状況を調べたいとき
  • 接続済みアプリのテーブルを管理したいとき
BuilderIO/agent-native7,5192026年10月26日 更新

BuilderIO のスキルをすべて見る

このスキルの問題を報告する