これは DuckDB Advent Calendar 2025 の1日目の記事です。
DuckDB最高ですね🦆
- WASMによりブラウザだけで使えるDuckDB Shellはもっと最高です
- 怠惰(Laziness)とは、Perlで有名なラリー・ウォール氏の言う『プログラマーの三大美徳』の1つですが、楽するともっと楽したくなるのが人間です
概要
gsheetsはDuckDB Shellで使えない!?
人気のコミュニティ拡張gsheetsは、大変便利なものですが、DuckDB Shell では使えないぽいです。
Google Sheetsでテーブルデータを眺めながら、DuckDB Shell だけで楽したい人は今回の内容を試してみてください。
DuckDB Shell でなくとも、DuckDBインストールOKよ!という方は、gsheets試してみてください。
INSTALL gsheets FROM community;
LOAD gsheets;
CREATE SECRET (TYPE gsheet);
・参考URL
https://duckdb.org/community_extensions/extensions/gsheets
https://duckdb.org/2025/02/26/google-sheets-community-extension
どんな感じになるのか
- Google SheetsはCSVダウンロードURLを発行できる
- DuckDBでは、CSVファイルからSELECTできる
-
READ_CSV_AUTOでスキーマ推論し、分析用のテーブルsheetを作成 - 今回は地理空間情報向けに
spatial拡張も有効にしています
ちなみにデータは、国交省のProject LINKSで公開されているオープンデータ貨物自動車運送事業データを使っています。
1. 分析したいスプレッドシートを開く
2. URLが生成されるのでクリックする
3. DuckDB Shellが起動して、CTASが実行される
スプレッドシートのデータをもとに、sheetsテーブルが作成されるので、あとはsheetsテーブルに対してSQLを書けばOK👼
手順
1. Google Sheetsの準備
- Google Sheet上に分析したいCSVファイルをupしておく
- 右上の「共有」を押し、URLを限定公開する
2. GAS appsscript.jsonマニフェストを設定する
appsscript.jsonにaddOnsを追加する
- マニフェストファイルを編集するには、「プロジェクトの設定」から「appsscript.json」マニフェスト ファイルをエディタで表示する」をチェック
-
onHomepageを使って、サイドバーに出るようにする - アイコンURLは、GoogleのMaterial Iconsなどから適当に選ぶ
✅appsscript.json の設定例
{
"timeZone": "Asia/Tokyo",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"addOns": {
"common": {
"name": "DuckDB Shell",
"logoUrl": "{アイコンのURL}"
},
"sheets": {
"homepageTrigger": {
"runFunction": "onHomepage"
}
}
}
}
3. GAS スクリプトを作成してデプロイする
- Marketplaceに登録しなくてもOK
- デプロイ > 「デプロイをテスト」 を選択し、種類の選択で「エディタアドオン」を選択
- あとは「インストール」して、スプレッドシートのサイドメニューから選ぶだけ!
✅GASスクリプトの例
function onHomepage(e) {
const card = CardService.newCardBuilder()
.setHeader(CardService.newCardHeader().setTitle("DuckDB Shell URL Generator"))
.addSection(
CardService.newCardSection()
.addWidget(
CardService.newTextButton()
.setText("DuckDB ShellのURLを生成")
.setOnClickAction(
CardService.newAction()
.setFunctionName("handleGenerateDuckUrl")
)
)
)
.build();
return CardService.newActionResponseBuilder()
.setNavigation(CardService.newNavigation().pushCard(card))
.build();
}
function handleGenerateDuckUrl(e) {
const url = buildDuckDBShellUrl();
const card = CardService.newCardBuilder()
.addSection(
CardService.newCardSection()
.addWidget(
CardService.newTextParagraph().setText(
`<b>さぁ、カモ🦆りなさい:</b><br><a href="${url}">${url}</a>`
)
)
)
.build();
return CardService.newActionResponseBuilder()
.setNavigation(CardService.newNavigation().pushCard(card))
.build();
}
// アクティブなシートの CSV エクスポート URL を返す
function buildgsheetsUrl() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
const spreadsheetId = ss.getId();
const gid = sheet.getSheetId();
const url = `https://docs.google.com/spreadsheets/d/${spreadsheetId}/export?format=csv&gid=${gid}`;
return url;
}
function buildDuckDBShellUrl() {
const csvUrl = buildgsheetsUrl();
const csvUrlReplaced = csvUrl.replace(/-/g, ' ');
const csvUrlEncoded = encodeURIComponent(csvUrlReplaced);
const duckBase =
"https://shell.duckdb.org/#queries=" +
"v0,load-spatial~,load-httpfs~,CREATE-TABLE-sheet-AS-SELECT-*-FROM-READ_CSV_AUTO('";
const duckSuffix = "')~DESC-sheet~";
const duckUrl = duckBase + csvUrlEncoded + duckSuffix;
SpreadsheetApp.getUi().alert("🦆URL:", duckUrl, SpreadsheetApp.getUi().ButtonSet.OK);
return duckUrl;
}
まとめ
-
DuckDBステキです🦆 - ブラウザでSELECTできちゃうなんて
DuckDB-Wasm最高🦆 - これで、帰省しても、いつでもどこでも🦆三昧




