😊

sqlmeshはdbtの競合になり得るか

に公開

はじめに

群雄割拠のModern Data Stackの中でも、データ変換 (transoform) 領域は珍しく一強状態で、dbtがトップの地位を固めつつあります。

日本で次に知名度があるのがdataformですが、BigQuery専用のプロダクトとなりアップデートのペースも緩やかなため、競合としては機能していません。

では、dbtの競合は他にないのかというと、まだsqlmeshというプロダクトが存在します。

Github star historyを見ても、すでにdataformは追い抜いていて、順調に数字を伸ばしています。

開発元は2022年に設立されたTobiko Dataというアメリカのスタートアップで、ロゴを見る限り日本語の「とびこ」に由来しているようです。Founderは日本人ではないようですが、アメリカにも日本食レストランはいっぱいありますから馴染みがあるのかもしれません。

sqlmesh以外にも、sqlglotと呼ばれるpythonベースのSQLパーサーや、マネージドサービスのCloud版も提供しており、最近dbt-fusionをローンチした点も考慮するとdbtとほぼ同じビジネスモデルであることが分かります。

ちなみに、sqlmeshとsqlglotは、どちらもオープンソースで、GitHub上で公開されています。パーサーのsqlglotの方が色々な使い方ができるからか、sqlmeshよりスター数が多いのが面白いですね。

https://github.com/TobikoData/sqlmesh

https://github.com/tobymao/sqlglot

では、sqlmeshにはdbtと比較してどのような違い・特徴があるのでしょうか。ざっとドキュメントを読んでみたところ、表現がやや正確ではないかもしれませんが、概ね以下の2点にまとめられそうでした。

  • Virtual Data Environmentsと呼ばれる独自の環境分離による、データパイプラインのバージョニングとCI/CD
  • Terraformっぽいデプロイ方法

早速順番に見ていきましょう。

Virtual Data Environments とは

sqlmeshの最大の特徴は、Virtual Data Environmentsと呼ばれる独自の環境分離の仕組みです。

以下の画像が一番分かりやすかったので、引用して紹介します。


出展:https://www.tobikodata.com/blog/virtual-data-environments

まず、Virtual Data EnvironmentsにはVirtual LayerとPhysical Layerの2つのレイヤーが存在します。ざっくり言うと、Virtual LayerはViewでPhysical Layerは実際のテーブルとなっているため、Virtual Layerから参照するテーブルを変えるという方法でデプロイが行われます。

最初はproduction環境しかありませんが(1)、改修のためにdevelopment環境を作成することができます(2)。このとき、それぞれの環境はまだ同じデータを参照しています。

ここで開発環境でモデルAに変更を加えてデプロイすると、開発環境だけデプロイされた新しいスナップショット2を参照するようになります(3)。

最後に、開発環境での検証が済めば本番環境へのデプロイになりますが、ここで変更があったモデルAの参照テーブルをスナップショット1からスナップショット2に変更するという形で、開発環境での変更が本番環境に反映されます(4)。

この方法のメリットとして、

  • 開発環境で検証済のデータをそのまま本番環境に反映できるため、再作成の必要がなく、DWHのコストを抑えられる
  • incrementalモデルで使用するデータの環境間差異がなくなるため、特定の環境でだけ発生するバグを防げる

などが挙げられます。

ただ一方で、環境間で同じリソースを共有することになるので、本番環境へのアクセス権限を制限することが難しくなるというデメリットもありそうです。

Terraformっぽいデプロイ方法

次に、先ほどのVirtual Data Environmentsを用いた開発サイクルが実際にどのようなコマンドで実行されるのかを見ていきましょう。

チュートリアル用のデモ動画とGitHubリポジトリが公開されているので、そちらをベースに進めていきます。

https://sqlmesh.readthedocs.io/en/stable/examples/sqlmesh_cli_crash_course/

https://github.com/sungchun12/sqlmesh-cli-crash-course

sqlmeshプロジェクト内にあるsqlファイルは以下のような形になっています。

cronが定義可能だったり、auditsと呼ばれるテストをSQLファイル内に定義できるなど、独自の構文はいくつかありますが、dbtやdataform経験者であれば、それほど違和感なく読めるようなコードになっています。

full_model.sql
MODEL (
  name sqlmesh_example.full_model,
  kind FULL,
  cron '@daily',
  grain item_id,
  audits (assert_positive_order_ids),
);

SELECT
  item_id,
  COUNT(DISTINCT id) AS num_orders,
  new_column
FROM
  sqlmesh_example.incremental_model
GROUP BY item_id, new_column
incremental_model.sql
MODEL (
  name sqlmesh_example.incremental_model,
  kind INCREMENTAL_BY_TIME_RANGE (
    time_column event_date
  ),
  start '2020-01-01',
  cron '@daily',
  grain (id, event_date),
  audits( UNIQUE_VALUES(columns = (
      id,
  )), NOT_NULL(columns = (
      id,
      event_date
  ))),
  allow_partials true
);

SELECT
  id,
  item_id,
  event_date,
  16 as new_column
FROM
  sqlmesh_example.seed_model
WHERE
  event_date BETWEEN @start_date AND @end_date
seed_model.sql
MODEL (
  name sqlmesh_example.seed_model,
  kind SEED (
    path '../seeds/seed_data.csv'
  ),
  columns (
    id INTEGER,
    item_id INTEGER,
    event_date DATE
  ),
  grain (id, event_date)
);

このチュートリアルでは、incremental_modelとfull_modelの2つのモデルに加えた変更をデプロイしていきます。

まずは、開発環境にデプロイしていくため、terraformと同じ感覚でsqlmesh plan devコマンドを実行します。問題なければ、こちらもterraform同様、applyします。

> sqlmesh plan dev
Differences from the `dev` environment:

Models:
├── Directly Modified:
   ├── sqlmesh_example__dev.incremental_model
   └── sqlmesh_example__dev.full_model
└── Indirectly Modified:
    └── sqlmesh_example__dev.view_model

---

+++

@@ -9,7 +9,8 @@

 SELECT
   item_id,
   COUNT(DISTINCT id) AS num_orders,
-  6 AS new_column
+  new_column
 FROM sqlmesh_example.incremental_model
 GROUP BY
-  item_id
+  item_id,
+  new_column

Directly Modified: sqlmesh_example__dev.full_model (Breaking)

---

+++

@@ -15,7 +15,7 @@

   id,
   item_id,
   event_date,
-  5 AS new_column
+  7 AS new_column
 FROM sqlmesh_example.seed_model
 WHERE
   event_date BETWEEN @start_date AND @end_date

Directly Modified: sqlmesh_example__dev.incremental_model (Breaking)
└── Indirectly Modified Children:
    └── sqlmesh_example__dev.view_model (Indirect Breaking)
Models needing backfill:
├── sqlmesh_example__dev.full_model: [full refresh]
├── sqlmesh_example__dev.incremental_model: [2020-01-01 - 2025-04-16]
└── sqlmesh_example__dev.view_model: [recreate view]
Apply - Backfill Tables [y/n]: y

Updating physical layer ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% 2/2 0:00:00

 Physical layer updated

[1/1]  sqlmesh_example__dev.incremental_model               [insert 2020-01-01 - 2025-04-16]                 0.03s
Executing model batches ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0% pending 0:00:00
sqlmesh_example__dev.incremental_model .
[WARNING] sqlmesh_example__dev.full_model: 'assert_positive_order_ids' audit error: 2 rows failed. Learn more in logs:
/Users/sung/Desktop/git_repos/sqlmesh-cli-revamp/logs/sqlmesh_2025_04_18_10_33_43.log
[1/1]  sqlmesh_example__dev.full_model                      [full refresh, audits ❌1]                       0.01s
Executing model batches ━━━━━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━ 33.3% 1/3 0:00:00
sqlmesh_example__dev.full_model .
[WARNING] sqlmesh_example__dev.view_model: 'assert_positive_order_ids' audit error: 2 rows failed. Learn more in logs:
/Users/sung/Desktop/git_repos/sqlmesh-cli-revamp/logs/sqlmesh_2025_04_18_10_33_43.log
[1/1]  sqlmesh_example__dev.view_model                      [recreate view, audits ✔2 ❌1]                   0.01s
Executing model batches ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% 3/3 0:00:00

 Model batches executed

Updating virtual layer  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% 3/3 0:00:00

 Virtual layer updated

ここまでで、先ほど紹介した画像の(3)までが完了した状態になりました。

最後に、本番環境へのデプロイを行います。実際の運用ではGitHub Actionなどの使用が推奨されていますが、ローカルからもdevと同じようなコマンドでデプロイ可能です。

> sqlmesh plan
Differences from the `prod` environment:

Models:
├── Directly Modified:
   ├── sqlmesh_example.full_model
   └── sqlmesh_example.incremental_model
└── Indirectly Modified:
    └── sqlmesh_example.view_model

---

+++

@@ -9,7 +9,8 @@

SELECT
  item_id,
  COUNT(DISTINCT id) AS num_orders,
-  5 AS new_column
+  new_column
FROM sqlmesh_example.incremental_model
GROUP BY
-  item_id
+  item_id,
+  new_column

Directly Modified: sqlmesh_example.full_model (Breaking)

---

+++

@@ -15,7 +15,7 @@

  id,
  item_id,
  event_date,
-  5 AS new_column
+  7 AS new_column
FROM sqlmesh_example.seed_model
WHERE
  event_date BETWEEN @start_date AND @end_date

Directly Modified: sqlmesh_example.incremental_model (Breaking)
└── Indirectly Modified Children:
    └── sqlmesh_example.view_model (Indirect Breaking)
Apply - Virtual Update [y/n]: y

SKIP: No physical layer updates to perform

SKIP: No model batches to execute

Updating virtual layer  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% 3/3 0:00:00

 Virtual layer updated

開発環境で既にphysical layerが更新されているため、前述した通りに本番環境では処理がスキップされ、virtual layerの更新のみが行われていることが、ログの最後の箇所から確認できます。

その他の機能

sqlmeshには他にもたくさんの機能があるので、いくつかピックアップして紹介したいと思います。

Airflowっぽいスケジューリングとステート管理

INCREMENTAL_BY_TIME_RANGEと呼ばれるモデルタイプでは、MODELブロック内に実行開始日とcronを定義することができます。

この概念はAirflowと非常によく似ており、cronで指定されたintervalごとに実行結果が保存されます。

そのため、未実行の期間をキャッチアップさせる、過去の特定の期間だけ実行させるなどのような、Airflowに近い運用を行うことができます。


出展:https://sqlmesh.readthedocs.io/en/latest/guides/incremental_time/#calculating-intervals

ユニットテストの自動生成

dbtにも欲しいと思った機能の1つがユニットテストの自動生成です。

以下のコマンドのように、実テーブルのデータを元にinputとoutputのデータを自動生成して、ユニットテストを作成することができます。

sqlmesh create_test sqlmesh_example.full_model \
  --query sqlmesh_example.incremental_model \
  "select * from sqlmesh_example.incremental_model limit 5"
test_full_model:
  model: '"db"."sqlmesh_example"."full_model"'
  inputs:
    '"db"."sqlmesh_example"."incremental_model"':
    - id: -11
      item_id: -11
      event_date: 2020-01-01
      new_column: 7
    - id: 1
      item_id: 1
      event_date: 2020-01-01
      new_column: 7
    - id: 3
      item_id: 3
      event_date: 2020-01-03
      new_column: 7
    - id: 4
      item_id: 1
      event_date: 2020-01-04
      new_column: 7
    - id: 5
      item_id: 1
      event_date: 2020-01-05
      new_column: 7
  outputs:
    query:
    - item_id: 3
      num_orders: 1
      new_column: 7
    - item_id: 1
      num_orders: 3
      new_column: 7
    - item_id: -11
      num_orders: 1
      new_column: 7

ローカル環境で試したい場合

お手元の環境にインストールしたい場合は、こちらのドキュメントを参考にしてください。

dbt projectをベースにsqlmeshのプロジェクトを構成することもできるので、既存のdbtプロジェクトで試してみる、ということも可能です。

最後に

sqlmeshは既に非常に多くの機能を持つリッチなツールですが、まだバージョニングはv0であり、毎日のようにリリースが行われています。

先行者だったdbtは後追いでsqlmeshのアーキテクチャと機能を採用する形になっており、dbt-fusionで巻き返しを図ろうとしています。結果として、SQLパーサーとSQLワークフローエンジンを統合するというアーキテクチャも両者で非常に似通ってきており、今後は機能面での差別化が難しくなっていくでしょう。

現時点でのsqlmeshの採用余地としては、dbtがエンタープライズ化・高価格化を進めていることもあり、dbt-coreでは実現できない高品質なデータパイプラインを低コストで構築したいケースなどが考えられます。

一方で、sqlmeshにはVirtual Data Environmentsなどエンジニアリング要素の強い独自の概念があり、使いこなすには他のツール以上の学習コストがかかります。データアナリストなど非エンジニアと協業するケースでは、現時点ではややハードルが高いかもしれません。

もう1つ気になっているのは、sqlmeshのマネタイズ戦略です。dbtがオープンソースであることを諦め別のビジネスモデルを模索している中で、sqlmeshが同じようなオープンソースモデルでどのように収益化を図っていくのか、明確な戦略は今のところ読み取ることができませんでした。

とはいえ、1つのプロダクトが独占的な地位を乱用するような状態よりも、sqlmeshのような競合が登場し、選択肢が増えることは利用者にとって歓迎すべきことです。既にdbtを利用しているのであれば今すぐ乗り換える必要はありませんが、今後のsqlmeshの進化と業界の動向には注目していきたいと思います。

Discussion