---
canonical: "https://agi-labo.com/tools/wings/docs/databases-sql"
title: "Database と SQL"
description: "Database を作り、許可された SQL で読み書きします。"
section: "主要機能"
order: 8
pageType: "how-to"
lastVerifiedVersion: "1.4.16"
lastVerifiedAt: "2026-10-15"
draft: false
surfaces: ["cli", "console", "api"]
plans: ["free", "member"]
---

Database を作ると、CLI や Handler から SQL でデータを読み書きできます。
まず小さなメモの表を作って、保存と読み取りを試します。

## 1. 表を作り、データを保存する

```sh
wings db create notes
wings db exec notes "CREATE TABLE notes (id INTEGER PRIMARY KEY, text TEXT)"
wings db exec notes "INSERT INTO notes (text) VALUES (?)" --params '["Hello"]'
wings db query notes "SELECT id, text FROM notes ORDER BY id DESC LIMIT 10"
```

`text` が `Hello` の行が表示されれば、保存できています。
すでに `notes` という Database がある場合は、別の名前で試してください。

## 2. Handler のコードを保存する

次を `notes-reader.js` として保存します。

```js
export default async function (event, env) {
  return env.db.query('SELECT id, text FROM notes WHERE id = ?', [event.id])
}
```

## 3. Handler を登録して確認する

```sh
wings handler deploy --name notes-reader --file notes-reader.js --db notes
wings handler list
```

一覧の ID を使って実行します。

```sh
wings run <handler-id> --event '{"id":1}'
wings handler logs <handler-id>
```

最初に保存した `Hello` の行が返り、Run が成功になれば確認完了です。

## 使える SQL と、使えないときの対処

1回の呼び出しで実行できるのは1文です。
読み取れるテーブルは最大1つです。
自分で作ったテーブルだけを使えます。
値は SQL に直接埋め込まず、上の例のように `?` とパラメーターで渡してください。

使えない SQL は実行前に `db_sql_forbidden` で拒否します。
`reason` に理由、メッセージに代わりの方法を示します。

- 複数文をまとめるには `env.db.batch()` を使います。
- JOIN の代わりに、別々の query の結果を JavaScript で組み合わせます。
- サブクエリの代わりに、先に値を取得して `IN (?, ?)` へ渡します。
- `RETURNING` の行を読むには query を使います。exec と batch は件数だけを返します。

## 容量と実行時間

容量はアカウント全体の合計で数えます。
上限を超える書き込みは取り消します。
読み取り、容量が増えない書き込み、Database の削除は引き続き使えます。

DELETE で空いた領域は後で再利用されます。
ただし、表示される使用容量が減るとは限りません。
プランを下げた場合は、作成が古い Database から上限数まで書き込めます。
残りは読み取り専用です。

DB だけの1日の時間枠はありません。
CLI や Console からの操作も[1日の利用時間](/tools/wings/docs/run-duration)に数えます。
操作が待たされる場合や一時停止中は、`db_time_quota_exceeded` の案内を確認してください。

## SQL リファレンス

ここからは、文や関数の対応範囲を調べるための一覧です。
初めて使う場合は、上の3つの手順までで読み書きを試せます。

### 文と関数

使える文は `SELECT`、`INSERT`、`REPLACE`、`UPDATE`、`DELETE`、`CREATE TABLE`、`CREATE INDEX`、`ALTER TABLE`、DROP、EXPLAIN です。
SELECT は1つのテーブル、または `json_each()` / `json_tree()` を読み取れます。
集計には DISTINCT、GROUP BY、HAVING を使えます。
対応する Window 関数には `OVER (PARTITION BY ... ORDER BY ...)` を使えます。

| 種類 | 関数 |
| --- | --- |
| 一般 | `abs`, `changes`, `char`, `coalesce`, `concat`, `concat_ws`, `glob`, `ifnull`, `iif`, `instr`, `last_insert_rowid`, `length`, `like`, `likelihood`, `likely`, `lower`, `ltrim`, `nullif`, `octet_length`, `random`, `round`, `rtrim`, `sign`, `substr`, `total_changes`, `trim`, `typeof`, `unhex`, `unicode`, `unlikely`, `upper` |
| 日時 | `date`, `time`, `datetime`, `julianday`, `unixepoch`, `strftime`, `timediff` |
| 数学 | `acos`, `acosh`, `asin`, `asinh`, `atan`, `atan2`, `atanh`, `ceil`, `cos`, `cosh`, `degrees`, `exp`, `floor`, `ln`, `log`, `log10`, `log2`, `mod`, `pi`, `pow`, `radians`, `sin`, `sinh`, `sqrt`, `tan`, `tanh`, `trunc` |
| JSON | `json`, `json_array`, `json_array_length`, `json_error_position`, `json_extract`, `json_insert`, `json_object`, `json_quote`, `json_remove`, `json_replace`, `json_set`, `json_type`, `json_valid`, `jsonb`, `jsonb_array`, `jsonb_extract`, `jsonb_insert`, `jsonb_object`, `jsonb_remove`, `jsonb_replace`, `jsonb_set` |
| 集計 | `count`, `sum`, `total`, `avg`, `min`, `max` |
| Window | `row_number`, `rank`, `dense_rank`, `percent_rank`, `cume_dist`, `ntile`, `lag`, `lead` |

別名は元の関数と同じルールで確認します。
`substring` は `substr`、`ceiling` は `ceil`、`power` は `pow` として扱います。
`format` は `printf`、`string_agg` は `group_concat` として扱うため使えません。

### 式と行数の上限

`trim`、`ltrim`、`rtrim`、`unhex` は1引数だけ使えます。
`instr` の第2引数は64文字以下の文字列リテラルにします。
LIKE / GLOB のパターンは50バイトまでです。
文字連結には `||`、`concat`、`concat_ws` を使います。

| 対象 | 上限 |
| --- | --- |
| 1操作で走査する行 | 10,000行 |
| 1操作で返す行 | 1,000行 |
| 1文の LIKE / GLOB / like() / glob() / instr() の合計 | 2回 |
| 1文の文字連結 | 8回 |
| 1文の関数呼び出し（`->` / `->>` を含む） | 16回 |
| 1文で列や式の値同士を比べる回数 | 16回 |
| 1つの IN に並べる値 | 100個 |
| 1つの CASE の WHEN | 32分岐 |
| batch 全体のパターン比較 | 4回 |
| batch 全体の文字連結 | 16回 |
| batch 全体の関数呼び出し | 64回 |
| batch 全体で列や式の値同士を比べる回数 | 64回 |

式の上限を超えた場合の `reason` は `expression_limit` です。
複数列を検索する場合は、Handler で検索用の列を作り、その列に LIKE を1回使ってください。
ID は `crypto.randomUUID()` などで Handler 側で作ります。

### テーブル定義と外部キー

自動計算する列（generated column）、式を使う index、条件付きの index は使えません。
CHECK は、列や単純な関数を定数と比べる式などに限ります。
たとえば `CHECK (length(name) <= 100)` は使えます。
IN、BETWEEN、NULL 判定、NOT / AND / OR も使えます。

CHECK で使える関数は `length`、`octet_length`、`typeof`、`abs`、`lower`、`upper` と、1引数の `trim`、`ltrim`、`rtrim` です。
1文の CHECK は比較32回、関数8回、列や式の値同士の比較4回までです。
古い定義がこの制限に当たる表は読めますが、書き込めません。
問題の index を削除するか、新しい表にデータを移してください。
`ALTER TABLE ... ADD COLUMN` も、この制限では書き込みとして扱います。

外部キーの子列には、親キーの型・照合順序に合う index が必要です。
たとえば INTEGER の親キーには INTEGER の子列を使います。
`foreign_key_index` のメッセージに、必要な CREATE INDEX を示します。
遅延外部キーは使えません。

### 対応していないもの

VIEW への読み書きと、TRIGGER を持つ表への書き込みは拒否します。
VIEW が参照しているだけの通常の表は、書き込み禁止にはなりません。
古い VIEW や TRIGGER は DROP で削除できます。

JOIN、WITH、サブクエリ、複合 SELECT、集計 Window 関数、`first_value`、`last_value`、`nth_value`、名前付き Window、Window frame は使えません。
virtual table、PRAGMA、ATTACH、VACUUM、ANALYZE、REINDEX、トランザクション文にも対応していません。
`sqlite_` と `_cf_` で始まる名前は予約されています。
組み込みの `sqlite_schema`、`sqlite_master`、`sqlite_stat1`、`sqlite_sequence`、`pragma_*` は読めません。

次の関数は使えません。

`json_group_array`, `json_group_object`, `jsonb_group_array`, `jsonb_group_object`, `group_concat`, `printf`, `replace`, `hex`, `quote`, `randomblob`, `zeroblob`, `json_pretty`, `json_patch`, `jsonb_patch`
