クエリ D1
D1はSQLiteのクエリエンジンを活用しているため、ほとんどのSQLiteのSQL規約と互換性があります。D1クライアントAPIを使用すると、Worker内からD1データベースと対話できます。
D1クライアントAPIは、準備済みおよび静的ステートメントをサポートしています。ベストプラクティスは、SQLを実行するためにデータベースによって使用される事前コンパイルされたオブジェクトである準備済みステートメントを使用することです。これは、準備済みステートメントが全体的に実行速度を向上させ、SQLインジェクション攻撃を防ぐためです。
以下は、準備済みステートメントの例です:
const stmt = db.prepare('SELECT * FROM users WHERE name = ?1').bind('Joe');ただし、静的ステートメントを使用することを選択した場合は、以下のように使用できます:
const stmt = db.prepare('SELECT * FROM users WHERE name = "John Doe"');D1は、準備済みステートメントのパラメータバインディングに関してSQLiteの規約 ↗に従います。現在、D1はOrdered(?NNNN)およびAnonymous(?)パラメータのみをサポートしています。将来的には、名前付きパラメータもサポートされる予定です。
| 構文 | タイプ | 説明 |
|---|---|---|
?NNN | Ordered | 数字NNNの後に疑問符が続くと、NNN番目のパラメータのためのスポットが保持されます。NNNは1からSQLITE_MAX_VARIABLE_NUMBERの間でなければなりません。 |
? | Anonymous | 数字の後に続かない疑問符は、すでに割り当てられた最大のパラメータ番号よりも1大きい番号のパラメータを作成します。これにより、パラメータ番号がSQLITE_MAX_VARIABLE_NUMBERを超える場合はエラーになります。このパラメータ形式は、他のデータベースエンジンとの互換性のために提供されています。しかし、疑問符を誤って数えるのが簡単なため、このパラメータ形式の使用は推奨されません。プログラマーは、以下のいずれかの記号形式または上記の?NNN形式を使用することを推奨します。 |
パラメータをバインドするには、stmt.bind()メソッドを使用します。
const stmt = db.prepare('SELECT * FROM users WHERE name = ?').bind( 'John Doe' );const stmt = db.prepare('SELECT * FROM users WHERE name = ? AND age = ?').bind( 'John Doe', 41 );const stmt = db.prepare('SELECT * FROM users WHERE name = ?2 AND age = ?1').bind( 41, 'John Doe' );D1は、クライアントAPIを介してパラメータとして渡されたサポートされているJavaScript(TypeScriptを含む)タイプを自動的に関連するD1タイプに変換します。型変換は以下の通りです:
| JavaScript | D1 |
|---|---|
| null | NULL |
| Number | REAL |
| Number 1 | INTEGER |
| String | TEXT |
| Boolean 2 | INTEGER |
| ArrayBuffer | BLOB |
| undefined | サポートされていません。undefined値を持つクエリはD1_TYPE_ERRORを返します。 |
1 D1は内部的に64ビット符号付きINTEGER値をサポートしていますが、BigInts ↗は現在APIでサポートされていません。JavaScriptの整数はNumber.MAX_SAFE_INTEGER ↗まで安全です。
2 ブール値はINTEGER型にキャストされ、1はTRUE、0はFALSEです。
stmt.all()およびdb.batch()メソッドは、結果(該当する場合)、成功ステータス、および操作の内部持続時間をミリ秒単位で含むメタオブジェクトを持つ型付きD1Resultオブジェクトを返します。
{ results: array | null, // 空の場合は[]、該当しない場合はnull success: boolean, // 操作が成功した場合はtrue、そうでない場合はfalse meta: { duration: number, // 操作の持続時間(ミリ秒単位) rows_read: number, // このクエリによって読み取られた(スキャンされた)行数 rows_written: number // このクエリによって書き込まれた行数 }}例:
const { duration } = (await db.prepare('INSERT INTO users (name, age) VALUES (?1, ?2)').bind( "John", 42 ).run()).meta;
console.log(duration); // 0.172db.exec()メソッドはD1ExecResultオブジェクトを返します:
{ count: number, // 実行されたクエリの数 duration: number // 操作の持続時間(ミリ秒単位)}D1クライアントAPIは、D1データベースに対してクエリを実行するための以下のクエリステートメントメソッドをサポートしています:
await stmt.all()await stmt.raw()await stmt.first( [column] )await stmt.run()await db.dump()await db.exec()
すべての行をオブジェクトの配列として返し、各結果行はD1Result型のresultsプロパティにオブジェクトとして表されます。
同じ列名を持つテーブルを結合する場合、左側の列のみが行オブジェクトに含まれます。すべての行を配列の配列として返すには、stmt.raw()を使用します。
const stmt = db.prepare('SELECT name, age FROM users LIMIT 3');const { results } = await stmt.all();console.log(results);/*[ { name: "John", age: 42, }, { name: "Anthony", age: 37, }, { name: "Dave", age: 29, }, ]*/TypeScriptを使用する場合、all()に型パラメータを渡して型付き結果オブジェクトを返すことができます。
結果を配列の配列として返し、各行は配列で表されます。戻り値の型は配列の配列であり、クエリメタデータは含まれません。
列名はデフォルトでは結果セットに含まれません。結果配列の最初の行に列名を含めるには、.raw({columnNames: true})を設定します。
const stmt = db.prepare('SELECT name, age FROM users LIMIT 3');const rows = await stmt.raw();console.log(rows);
/*[ [ "John", 42 ], [ "Anthony", 37 ], [ "Dave", 29 ],]*/
// columnNames: trueを使用した場合const stmt = db.prepare('SELECT name, age FROM users LIMIT 3');const [columns, ...rows] = await stmt.raw({columnNames: true});console.log(columns);
/*[ "name", age ], // 最初の結果配列には列名が含まれます*/TypeScriptを使用する場合、raw()に型パラメータを渡して型付き結果配列を返すことができます。
結果の最初の行を返します。これは、他のメソッドのようにメタデータを返しません。代わりに、オブジェクトを直接返します。
最初の行から特定の列を取得します:
const stmt = db.prepare('SELECT COUNT(*) AS total FROM users');const total = await stmt.first('total');console.log(total); // 50最初の行からすべての列を取得します:
const stmt = db.prepare('SELECT COUNT(*) AS total FROM users');const values = await stmt.first();console.log(values); // { total: 50 }クエリが行を返さない場合、first()はnullを返します。クエリが行を返しますが、columnが存在しない場合、first()はD1_ERROR例外をスローします。
stmt.first()はSQLクエリを変更しません。パフォーマンスを向上させるために、ステートメントにLIMIT 1を追加することを検討してください。
TypeScriptを使用する場合、first()に型パラメータを渡して型付き結果オブジェクトを返すことができます。
クエリ(またはクエリ)を実行し、結果を返します。すべての行をオブジェクトの配列として返し、各結果行はD1Result型のresultsプロパティにオブジェクトとして表されます。UPDATE、DELETE、INSERTなどの書き込み操作の場合、resultsは空になります。
Runは機能的にstmt.all()と同等であり、エイリアスとして扱うことができます。
const stmt = await db.prepare('SELECT name, age FROM users LIMIT 3')const { results } = await stmt.run();console.log(results);/*[ { name: "John", age: 42, }, { name: "Anthony", age: 37, }, { name: "Dave", age: 29, }, ]*/TypeScriptを使用する場合、run()に型パラメータを渡して型付き結果オブジェクトを返すことができます。
D1データベース全体をSQLite互換ファイルとしてArrayBuffer内にダンプします。
const dump = await db.dump()return new Response(dump, { status: 200, headers: { 'Content-Type': 'application/octet-stream' }});準備済みステートメントやパラメータバインディングなしで、1つまたは複数のクエリを直接実行します。このメソッドはパフォーマンスが低下する可能性があります(準備済みステートメントは場合によって再利用できるため)し、さらに重要なことに、安全性が低くなります。このメソッドは、メンテナンスや一時的なタスク(例えば、マイグレーションジョブ)にのみ使用してください。入力は、\nで区切られた1つまたは複数のクエリです。
エラーが発生した場合、クエリとエラーメッセージを含む例外がスローされ、実行が停止し、さらにステートメントは実行されません。エラーを参照して詳細を学んでください。
const migration = await fetch('/migration.sql');const out = await db.exec(migration.text());console.log(out);/*{ count: 80, duration: 76}*/D1クライアントAPIは@cloudflare/workers-typesパッケージを介して完全に型付けされており、TypeScript APIの一部としてジェネリック型 ↗もサポートしています。ジェネリック型を使用すると、関数が処理しているデータの型を理解するためのオプションの型パラメータを提供できます。
クエリステートメントメソッド stmt.all(), stmt.raw(), stmt.first()を使用する際に、各データベース行を表す型を提供できます。D1のAPIは、正しい型の結果オブジェクトを返します。
例えば、OrderRow型をstmt.all()に型パラメータとして提供すると、デフォルトのRecord<string, unknown>型の代わりに型付きArray<OrderRow>オブジェクトが返されます:
// 行の定義type OrderRow = { Id: string; CustomerName: string; OrderDate: number;}
// アプリケーションの他の場所でconst result = await env.MY_DB.prepare("SELECT Id, CustomerName, OrderDate FROM [Order] ORDER BY ShippedDate DESC LIMIT 100").all<OrderRow>();準備済みステートメントは新しいバインディングで再利用できます:
const stmt = db.prepare('SELECT name, age FROM users WHERE age < ?1');const young = await stmt.bind(20).all();console.log(young);/*{ results: [...], success: true meta: { duration: 31, }}*/ const old = await stmt.bind(80).all();console.log(old);/*{ results: [...], success: true meta: { duration: 29, }}*/SQLのLIKE演算子を使用して検索を実行します:
const { results } = await env.DB.prepare( "SELECT * FROM Customers WHERE CompanyName LIKE ?") .bind("%eve%") .all();console.log("results: ", results);/*results: [...]*/バッチ処理は、単一の呼び出しで複数のSQLステートメントをデータベースに送信します。これにより、D1へのネットワーク往復の待機時間が短縮されるため、大きなパフォーマンス向上が期待できます。D1は自動コミットで動作します。私たちの実装は、リスト内の各ステートメントが順次、非同時に実行され、コミットされることを保証します。
バッチステートメントは SQLトランザクション ↗ です。シーケンス内のステートメントが失敗した場合、その特定のステートメントに対してエラーが返され、全体のシーケンスが中止またはロールバックされます。
バッチステートメントを送信するには、batch() に準備されたステートメントのリストを提供し、同じ順序で結果を取得します。
await db.batch([ db.prepare("UPDATE users SET name = ?1 WHERE id = ?2").bind( "John", 17 ), db.prepare("UPDATE users SET age = ?1 WHERE id = ?2").bind( 35, 19 ),]);同じ準備されたステートメントを再利用してバッチを構築できます:
const stmt = db.prepare("SELECT * FROM users WHERE name = ?1");
const rows = await db.batch([ stmt.bind("John"), stmt.bind("Anthony"),]);
console.log(rows[0].results);/*[ { name: "John Clemente", age: 42, }, { name: "John Davis", age: 37, }, ]*/console.log(rows[1].results);/*[ { name: "Anthony Hopkins", age: 66, }, ]*/D1は次の SQLite PRAGMA ↗ ステートメントをサポートしています:
| PRAGMA | 説明 |
|---|---|
table_list | スキーマ内のテーブルとビューに関する情報を返します。出力の各行に1つのテーブルが含まれます。 |
table_info | このPRAGMAは、指定されたテーブルの各列に対して1行を返します。結果セットの列には、列名、データ型、列がNULLを許可するかどうか、および列のデフォルト値が含まれます。 |
defer_foreign_keys | 外部キー制約の強制をクエリ、設定、またはクリアします。詳細については、外部キーの定義を参照してください。 |
他のPRAGMAはD1の実装の特性により無効になっています。
const r = await db.batch([ db.prepare("PRAGMA table_list"), db.prepare("PRAGMA table_info(my_table)"),]);console.log(r);/*[ { "results": [ { "schema": "main", "name": "my_table", "type": "table", "ncol": 3, "wr": 0, "strict": 0 }, ... ] }, { "results": [ { "cid": 0, "name": "cid", "type": "INTEGER", "notnull": 0, "dflt_value": null, "pk": 1 }, ... ] }]
*/stmt. および db. メソッドは、エラーが発生するたびに Errorオブジェクト ↗ をスローします。
例外をキャッチするには、Error.message の値をログに記録します。例えば、以下のコードには無効なキーワード - INSERTZ が含まれています。これは INSERT の代わりです:
try { // これは意図的な誤字です await db.exec("INSERTZ INTO my_table (name, employees) VALUES ()");} catch (e: any) { console.error({ message: e.message });}上記のコードは次のエラーメッセージをスローします:
{ "message": "D1_EXEC_ERROR: Error in line 1: INSERTZ INTO my_table (name, employees) VALUES (): sql error: near \"INSERTZ\": syntax error in INSERTZ INTO my_table (name, employees) VALUES () at offset 0"}D1は、拡張(詳細)エラーメッセージに加えて、次のエラー定数を返します:
| メッセージ | 原因 |
|---|---|
D1_ERROR | 一般的なエラー。 |
D1_TYPE_ERROR | 列と値の間に型の不一致がある場合に返されます。一般的な原因は、null の代わりに undefined 変数(サポートされていない)を提供することです。 |
D1_COLUMN_NOTFOUND | 列が見つかりません。 |
D1_DUMP_ERROR | データベースダンプエラー。 |
D1_EXEC_ERROR | 行 x での実行エラー: y エラー。 |