SQL トランザクションと ACID の説明: COMMIT、ROLLBACK、分離レベル
2 つのアカウント間でお金を移動している途中でサーバーに障害が発生した場合はどうなりますか?トランザクションがその答えです。実際の例でどのように機能するか、ACID の意味、分離レベルの選択方法をご覧ください。
- トランザクションはステートメントをグループ化して、すべてが同時に成功するか、すべてが同時に失敗するようにします。
COMMITは変更を永続的にし、ROLLBACKは変更をキャンセルします。- ACID は、原子性、一貫性、分離性、耐久性を表します。
- 分離を強化すると、さらなる異常が防止されますが、同時実行性が低下する可能性があるため、慎重に選択してください。
アシャからベンに 300 ルピーを移動するには、1 つのアカウントから減算し、もう 1 つのアカウントに追加するという 2 つの更新が必要です。それらの間でシステムが故障すると、お金は消えてしまいます。 SQL トランザクション は、両方のステップを 1 つの単位として扱うことで、これを不可能にするために存在します。
このガイドでは、小規模なアカウント テーブルでのコミット、ロールバック、失敗について説明します。これらの例は SQLite で実行し、表示された残高はその実行から得られたものです。基本的なコマンドはどのデータベースでも似ていますが、ロックと分離の動作は大きく異なるため、独自のデータベースのドキュメントを確認する場所を示します。
トランザクションとは何ですか?
トランザクションは、データベースが単一の論理操作として扱う 1 つ以上の SQL ステートメントのシーケンスです。これが私たちのテーブルです:
CREATE TABLE accounts (
id INT PRIMARY KEY,
owner TEXT,
balance INT CHECK (balance >= 0)
);
INSERT INTO accounts VALUES (1, 'Asha', 1000), (2, 'Ben', 200);
CHECK 制約は、残高が決してゼロを下回らないというルールであり、後で使用します。トランザクションを開始する構文はさまざまであることに注意してください。SQLite と PostgreSQL は BEGIN を受け入れますが、MySQL には START TRANSACTION があり、SQL Server には BEGIN TRANSACTION が使用されます。
明示的なトランザクションの外では、ほとんどのデータベースは autocommit を使用し、各ステートメントは終了するとすぐに保存されます。 Oracle は注目に値する例外です。Oracle のセッションは、コミットするまでトランザクション内に留まるからです。
COMMIT: 永続化します
BEGIN;
UPDATE accounts SET balance = balance - 300 WHERE id = 1;
UPDATE accounts SET balance = balance + 300 WHERE id = 2;
COMMIT;
-- Asha | 700
-- Ben | 500
COMMIT が実行されるまで、変更は暫定的なものです。正常に返されると、データベースはそれらが保存されることを保証します。両方のアカウントの合計は 1200 に留まり、これが演習の要点です。
ロールバック: すべてをキャンセルします
ROLLBACK はトランザクション開始以降のすべてを元に戻します。
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
SELECT owner, balance FROM accounts;
-- Asha | 600 (visible inside this transaction)
-- Ben | 500
ROLLBACK;
SELECT owner, balance FROM accounts;
-- Asha | 700 (back to where it was)
-- Ben | 500
接続が切断されたり、コミット前にサーバーがクラッシュした場合にもロールバックが自動的に行われるため、中途半端なトランザクションは存続しません。
ステートメントが失敗したとき
ここではより現実的なケースを示します。 Asha は 5000 を送金しようとしましたが、アカウントにはその送金がありません。 Ben への貸方が最初に実行され、次に借方が CHECK ルールに違反します。
BEGIN;
UPDATE accounts SET balance = balance + 5000 WHERE id = 2;
UPDATE accounts SET balance = balance - 5000 WHERE id = 1;
-- error: CHECK constraint failed: balance >= 0
ROLLBACK;
-- Asha | 700
-- Ben | 500
この取引がなければ、ベンは 5000 ドルを保管していたでしょうし、帳簿は間違っていたでしょう。重要な点が 1 つあることに注意してください。失敗したステートメントは必ずしもトランザクション自体をキャンセルするとは限りません。たとえば、SQLite と MySQL では、以前のステートメントが保留状態のままになる可能性があるため、コードでロールバックを発行する必要があります。 PostgreSQL は代わりにトランザクション全体を中止としてマークし、ロールバックするまでそれ以降のステートメントを拒否します。
アプリケーション コードでは、トランザクションとエラー処理を常に組み合わせてください。これは Python の考え方であり、Python 例外処理 のガイドで構文が説明されています。
try:
conn.execute("BEGIN")
conn.execute(debit_sql, values)
conn.execute(credit_sql, values)
conn.execute("COMMIT")
except Exception:
conn.execute("ROLLBACK")
raise
セーブポイント
セーブポイントはトランザクション内の位置をマークするため、作業の一部のみを元に戻すことができます。
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
SAVEPOINT bonus;
UPDATE accounts SET balance = balance + 999 WHERE id = 2;
ROLLBACK TO bonus;
UPDATE accounts SET balance = balance + 50 WHERE id = 2;
COMMIT;
-- Asha | 650
-- Ben | 550
999 ボーナスは取り消されましたが、残りのトランザクションは継続してコミットされました。これは、長いプロセスのオプションのステップに役立ちます。
ACID プロパティの説明
| プロパティ | 約束 | 私たちの転送では |
|---|---|---|
| 原子性 | オール・オア・ナッシング | 両方の更新が行われる、またはどちらも行われない |
| 一貫性 | ルールはその後も存続します | どのバランスも CHECK ルールを破ることはできません |
| 孤立 | 同時作業は妨げられません | 他の人には中途半端な転送は見えません |
| 耐久性 | コミットされたデータは障害後も存続します | COMMIT 後に停電しても何も失われません |
一貫性はよく誤解されています。データベースは、制約や外部キーなど、宣言したルールのみを強制します。コードが間違った金額を送金することを防ぐことはできません。耐久性は通常、コミットが確認される前に変更がディスクに安全に記録される先行書き込みログによって実現されます。
同時トランザクションでは何が起こるでしょうか?
1 つの SQLite ファイルに対して 2 つの接続を開きました。最初のトランザクションはトランザクションを開始し、まだコミットせずに行を 1 から 2 に更新しました。 2 番目の接続で確認された内容は次のとおりです。
Connection B reads: 1 -- no uncommitted data
Connection B writes: database is locked -- it has to wait
(A commits)
Connection B reads: 2 -- now it sees the change
SQLite では、一度に 1 つのライターしか許可されません。これは、多くのトランザクションが同時に異なる行を書き込むことができるサーバー データベースよりも単純です。ただし、原則は同じです。通常の設定では、他のセッションはコミットされていない変更を認識すべきではなく、競合する書き込みは待機する必要があります。私たちのテストでは、2 番目の接続を待機せずにすぐに放棄するように設定したため、エラーはすぐに表示されました。
異常を読み取る
トランザクションが重複すると、特定の問題が発生する可能性があります。 SQL 標準では、トランザクションが観察する可能性のあるものに関して次のように定義されています。
| 異常 | 何が起こるか |
|---|---|
| ダーティリード | 別のトランザクションがコミットしていない変更を読み取り、後でロールバックされる可能性があります |
| 反復不可能な読み取り | 1 つのトランザクションで行を 2 回読み取ると、その間に誰かがコミットしたため、異なる値が取得されます。 |
| ファントムリード | 同じクエリを 2 回実行すると、誰かが挿入または削除したために余分な行または欠落した行が取得されます。 |
もう 1 つの問題は、アップデートが失われたです。 2 つのセッションで残高 100 が読み取られ、それぞれが 10 を加算し、それぞれが 110 を書き込みました。答えが 120 になるはずのときに、1 つの更新が事実上消えてしまいました。
SQL分離レベル
分離レベルを使用すると、厳密性と引き換えに同時実行性を実現できます。 SQL 標準で各レベルで許可される内容は次のとおりです。
| レベル | ダーティリード | 反復不可能な読み取り | ファントムリード |
|---|---|---|---|
| コミットされていない読み取り | 可能 | 可能 | 可能 |
| 読み取りがコミットされました | 防止された | 可能 | 可能 |
| 反復可能な読み取り | 防止された | 防止された | 可能 |
| シリアル化可能 | 防止された | 防止された | 防止された |
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
正確な構文と動作はデータベースによって異なります。デフォルトも異なります。PostgreSQL と SQL Server はコミットされた読み取りを使用しますが、MySQL の InnoDB は反復可能な読み取りを使用します。データベースによっては、標準で要求されているよりも強力な保証を提供したり、あるレベルを別のレベルとして扱ったりすることがあります。たとえば、PostgreSQL では、コミットされていない読み取りはコミットされた読み取りと同じように動作します。特定の動作に依存する前に、必ずデータベースのドキュメントを読んでください。
ヒント: 更新が失われた場合は、UPDATE accounts SET balance = balance + 10 のように 1 つのステートメントで変更を加えます。以前の読み取りに基づいてアプリケーションで計算を実行すると、ギャップが生じます。
デッドロック
デッドロックは、2 つのトランザクションがそれぞれ、もう一方のトランザクションが必要とするロックを保持している場合に発生します。トランザクション A は行 1 をロックし、行 2 を必要としますが、トランザクション B は行 2 をロックし、行 1 を必要とします。どちらも続行できません。
PostgreSQL、MySQL、SQL Server などのデータベースはこれを検出し、エラーで 1 つのトランザクションをキャンセルします。これは通常の動作であり、アプリケーションはこれに備えて準備する必要があります。デッドロックを減らすには、常に一貫した順序で行を更新します (最小のアカウント ID から順に更新するなど)。トランザクションを短くし、犠牲者として選択されたときに再試行します。
ベストプラクティス
- トランザクションは短くしてください。長いトランザクションはロックを保持し、他のユーザーをブロックします。
- トランザクション内でユーザーや遅い API を決して待たないでください。この作業は開始前または開始後に実行してください。
- 常に失敗を処理します。 すべてのパスはコミットまたはロールバックで終了する必要があります。
- 制約を追加します。 データベースに強制する実際のルールを与えます。優れたテーブルデザインも役立ちます。
- 適切なインデックスを使用します。検索が高速になると、ロックが保持される時間が短くなります。
- 再試行には注意してください。 デッドロック時に再試行しますが、操作が 2 回適用されないように注意してください。
- 意図的に同時実行性をテストします。これらのバグの多くは負荷がかかっている場合にのみ現れるため、基本的な統合テストだけではバグを明らかにできない可能性があります。
まとめ
トランザクションは、いくつかの危険なステップを 1 つの安全な操作に変換します。ACID は、これを信頼できるものにする保証について説明します。ビジネス アクションが複数の行またはテーブルに触れるときは、それが途中で停止したらどうなるかを自問してください。答えが気になる場合は、トランザクションに含めてください。練習するには、ローカル データベースで 2 つのセッションを開き、自分で異常を生成してみます。
よくある質問
SQLにおけるトランザクションとは何ですか?
トランザクションとは、複数の SQL 文を 1 つの単位として扱うものです。 COMMIT ですべてを保存するか、ROLLBACK ですべてを取り消します。
データベースの ACID プロパティとは何ですか?
ACID は、原子性、一貫性、分離性、耐久性を表します。これらを組み合わせることで、トランザクションがオール オアナッシングであること、ルールが損なわれないこと、障害が発生しても影響を受けないことを保証します。
COMMIT と ROLLBACK の違いは何ですか?
COMMIT は、トランザクションで行われたすべての変更を永続的に保存します。 ROLLBACK はそれらをキャンセルし、データをトランザクション開始前の状態に戻します。
ダーティリードとは何ですか?
ダーティ リードとは、あるトランザクションが、別のトランザクションがまだコミットしていない変更を読み取ることです。他のトランザクションがロールバックした場合、読み取られたデータは実際には存在しなかったことになります。
どの分離レベルを使用すればよいですか?
データベースのデフォルトから始めますが、通常はこれで問題ありません。異常が許容できない重要なロジックにはシリアライズ可能を使用し、失敗したトランザクションを再試行できるようにしてください。
学び続ける
テスト、WordPress、または SEO についてサポートが必要ですか?
私たちは、チームがより優れたソフトウェアを出荷し、サイトを成長させるのを支援します。あなたが取り組んでいることを教えてください。明確な次のステップを提案します。
詳しいガイドはこちら テスト、AI、最新の開発
この記事は気に入りましたか?トピックを選択して、最新のチュートリアルに直接アクセスしてください。
AI 幻覚の説明: 言語モデルが物事をでっち上げる理由とそれを軽減する方法
AI 幻覚について説明しました。幻覚とは何か、言語モデルが幻覚を生成する理由、一般的な種類、グラウンディングとプロンプトで幻覚を軽減する方法、そして私たちが実行した簡単なチェックです。
記事を読む →DevOps環境変数の説明: .env ファイル、シークレット、および設定が正しく行われている
実行した Python の例を使用して環境変数について説明します。環境変数の読み取りと設定、すべての値が文字列である理由、.env ファイル、プロセスの継承、シークレット、検証について説明します。
記事を読む →ソフトウェア開発API レート制限の説明: アルゴリズム、ヘッダー、および 429 エラーの処理
API レート制限については、実行したシミュレーションで説明します。トークン バケット、固定ウィンドウ、スライディング ウィンドウ、429 応答、レート制限ヘッダー、バックオフによる再試行、およびテスト制限です。
記事を読む →テスト自動化Python でのモッキング:unittest.mock、patch、およびテストを嘘にする間違い
Python でのunittest.mockによるモック、テスト済み: モックとreturn_value、side_effect、名前が使用されている場所のパッチ、仕様と自動仕様、日付のモック、およびよくある間違い。
記事を読む →ソフトウェアテストコードカバレッジの説明: 回線カバレッジとブランチカバレッジ、および 100% の実際の意味
実際のcoverage.pyの実行でコードカバレッジを説明: 行、分岐、条件のカバレッジ、レポートの読み取り、100%のカバレッジでもバグを見逃す可能性がある理由、および賢明なターゲット。
記事を読む →データベースとSQLSQL における DELETE、TRUNCATE、DROP: 違い、ロールバック、およびそれぞれをいつ使用するか
DELETE、TRUNCATE、DROP について、テスト済み SQL を使用して説明します。それぞれの削除内容、ロールバック、トリガー、ID リセット、速度、PostgreSQL、MySQL、SQL Server、SQLite の違いについて説明します。
記事を読む →データベースとSQLSQL ビューの説明: 作成、使用、落とし穴の回避、例を示します
SQL ビューについて、実行したクエリとともに説明しました。ビューの作成、ライブ結果、列の非表示、パフォーマンス、更新可能なビューと具体化されたビュー、壊れた依存関係、ベスト プラクティスについて説明します。
記事を読む →ソフトウェア開発TypeScript インターフェイスと型: 実際の違い、コンパイラでテスト
TypeScript インターフェイスと型、実際のコンパイラでチェック: 両方でできること、片方だけでできること、宣言のマージ、拡張と交差、および従うべき簡単なルール。
記事を読む →ソフトウェア開発Python *args と **kwargs の説明: 柔軟な関数と明確な例
Python の *args と **kwargs について、実行したコードで説明しました。パッキングとアンパッキング、パラメーターの順序、キーワードのみの引数と位置のみの引数、デコレーター、一般的なエラーです。
記事を読む →ソフトウェア開発Python Lambda 関数: 構文、実際の用途、および使用すべきでない場合
Python のラムダ関数について、実行したコードとともに説明します。構文、キーによるソート、マップとフィルター、クロージャー、遅延バインディング トラップ、通常の def がより適切な選択である場合。
記事を読む →人工知能AIエージェントとは何ですか?シンプルなエージェント ループを実行できる仕組み
AIエージェントとは何ですか?エージェント ループ、ツール、メモリ、ガードレール、実行した小さな Python ループ、実際のリスク、およびシンプルなチャットボットがより良い選択である場合についてのわかりやすいガイドです。
記事を読む →DevOpsモニタリングと可観測性: ログ、メトリクス、トレースによる本当の違い
モニタリングと可観測性について説明: 既知および未知の障害、ログ、メトリクスとトレース、チェックアウト インシデントのウォークスルー、ゴールデン シグナル、OpenTelemetry、および便利なアラート。
記事を読む →DevOpsDocker ボリュームとバインド マウント: コンテナ データを正しい方法で保持する
Docker ボリュームとバインド マウントについて説明します。コンテナ データが消える理由、名前付きボリューム、バインド マウント、tmpfs、Compose の例、バックアップ、アクセス許可、およびそれぞれをいつ使用するかについて説明します。
記事を読む →テスト自動化TDD と BDD: 主な違い、1 つの機能が両方の方法で構築され、それぞれをいつ使用するか
TDD と BDD は、両方の方法で構築された 1 つの割引機能で説明されています。実行したコードによるレッド-グリーン-リファクタリング、Given-When-Then シナリオ、ツール、トレードオフ、およびそれぞれをいつ使用するかです。
記事を読む →ソフトウェアテストユーザー受け入れテスト (UAT): プロセス、チェックリスト、および実際の例
ユーザー受け入れテストの説明: 誰が実行するか、QA との違い、7 段階のプロセス、UAT テスト ケース テンプレート、チェックリスト、サインオフ基準、およびよくある間違い。
記事を読む →データベースとSQLSQL NULL 値の説明: IS NULL、COALESCE、NULLIF、および回避すべきトラップ
SQL の NULL 値について、実行したクエリで説明しました。3 つの値のロジック、IS NULL、COALESCE、NULLIF、集計、結合、並べ替えでの NULL の扱い方、およびそれが密かに引き起こすバグです。
記事を読む →データベースとSQLSQL CASE WHEN ステートメント: 構文、実際の例、およびよくある間違い
SQL CASE WHEN では、単純な検索フォーム、条件順序、SUM を使用したピボット、COUNT トリック、カスタム並べ替え、更新、回避すべき NULL トラップなど、実行したクエリを使用して説明しました。
記事を読む →ソフトウェア開発JWTとは何ですか? JSON Web トークンを実際の例で説明する
JWTとは何ですか? JSON Web トークンについて説明しました。3 つの部分、実行したコード内で構築および改ざんされたトークン、JWT とセッション、ストレージ、リフレッシュ トークン、およびセキュリティ上の間違いです。
記事を読む →ソフトウェア開発JavaScript イベントループの説明: コールスタック、マイクロタスク、タイマーと例
JavaScript イベント ループについて、実行したコードで説明しました。コール スタック、タスク キュー、マイクロタスク キュー、setTimeout 0 が即時ではない理由、ブロッキング、スターベーション、およびインタビュー クイズです。
記事を読む →ソフトウェア開発Python ジェネレーターと収量の説明: 実際の例でメモリを節約する
Python ジェネレーターと Yield について、実行したコードを使用して説明します。一時停止と再開の方法、メモリ節約の測定、パイプライン、無限シーケンス、Yield from、send、および一般的なトラップについて説明します。
記事を読む →人工知能埋め込みとは何ですか?ベクトルとコサインの類似度を簡単に説明
埋め込みとは何ですか?ベクトル、コサイン類似度、セマンティック検索に関する平易なガイド。私たちが実行した小さな例、一般的な使用法、モデルの選択、既知の制限が含まれています。
記事を読む →DevOpsBlue-Green デプロイメントと Canary デプロイメント: 違い、トレードオフ、および選択方法
ブルー グリーン デプロイメントとカナリア デプロイメントについて説明: それぞれの仕組み、コスト、リスクとロールバック、テストした安定したユーザー バケット化、データベース移行、ローリング アップデート、および選択方法。
記事を読む →テスト自動化不安定なテスト: 問題の原因とそれを永久に修正する方法
不安定なテストについては、私たちが実行したデモを使用して説明しました。主な原因、確認および診断する方法、タイミングの適切な修正、共有された状態と日付、隔離プロセスなどです。
記事を読む →ソフトウェアテスト負荷テストとストレス テスト: 違い、メトリクス、および k6 の例
ロード テストとストレス テストについて説明します。それぞれが何を証明するか、スパイク テストとソーク テスト、重要な指標、平均値が誤解を招く理由、K6 の例、テストの計画方法について説明します。
記事を読む →ソフトウェアテストテスト ケースとテスト シナリオ: 主な違い、例、およびそれぞれをいつ使用するか
テスト ケースとテスト シナリオの説明: 明確な定義、並べて表、シナリオからケースへの 1 つのパスワード リセット機能、テンプレート、およびそれぞれをいつ使用するか。
記事を読む →データベースとSQLSQL クエリの最適化: 実行プランを読み取り、タイミングを考慮して遅いクエリを修正
SQL クエリの最適化は 100 万行でテストされています。実行プランの読み取り、適切なインデックスの追加、検索対象外のフィルターの修正、複合インデックスとキーセットのページネーションの使用。
記事を読む →データベースとSQLSQL UNION と UNION ALL: 違い、例、テストされたパフォーマンス
SQL UNION と UNION ALL について、実行したクエリで説明しました。重複、両方に従うルール、実際のデータのバグ、測定されたパフォーマンス、INTERSECT と EXCEPT、およびそれぞれをいつ使用するかについて説明します。
記事を読む →ソフトウェア開発JavaScript Fetch API チュートリアル: GET、POST、エラー処理、タイムアウト
ローカル サーバーに対してテストされた JavaScript Fetch API チュートリアル: GET と POST、404 がスローされない理由、再利用可能なヘルパー、クエリ文字列、タイムアウト、CORS、およびよくある間違い。
記事を読む →ソフトウェア開発Python ファイル処理: 読み取り、書き込み、CSV、JSON、パスライブラリと例
Python のファイル処理について、実行したコードで説明します。オープン モード、大きいファイルの読み取り、CSV、JSON、パスライブラリ、バイナリ ファイル、安全な書き込み、エラー処理、およびよくある間違いについて説明します。
記事を読む →ソフトウェア開発Python のクラスとオブジェクト: 実際の例を含む OOP チュートリアル
Python のクラスとオブジェクトについて、実行したコードとともに説明します: __init__、メソッド、クラスとインスタンスの属性、継承、プロパティ、ダンダー メソッド、データクラス、およびよくある間違い。
記事を読む →人工知能大規模言語モデルとは何ですか? LLM の仕組みを誇大広告なしで説明
大規模言語モデルとは何ですか? LLM は、トークン、エンベディング、アテンション、トレーニング、温度およびコンテキスト ウィンドウ、小さな次の単語のデモ、実際の制限など、平易な言葉で説明されています。
記事を読む →DevOps初心者のための Linux コマンド: 例を含む基本ターミナル ガイド
初心者向けの Linux コマンド。ナビゲーション、ファイル、ログの表示、grep、パイプ、権限、プロセス、アーカイブなどのタスク別にグループ化されており、実用的なログ分析の例も含まれています。
記事を読む →テスト自動化Selenium と Playwright: 主な違い、コードの比較、どちらを選択するか
Selenium と Playwright の比較: アーキテクチャ、待機、ブラウザと言語のサポート、デバッグ、両方のツールでの同じテスト、移行のアドバイスと選択方法。
記事を読む →ソフトウェアテスト等価性分割: テスト設計手法を実際の例で説明
等価性パーティション化について説明しました。有効なパーティションと無効なパーティションを見つける方法、年齢、パスワード、クーポンに関する実際の例、実行したテスト、境界と組み合わせる理由。
記事を読む →ソフトウェアテストソフトウェアテストにおける重大度と優先度: 相違点、マトリックス、および実際の例
ソフトウェア テストにおける重大度と優先度について説明します。明確な定義、例を含む 4 方向のマトリックス、レベル スケール、それぞれの設定者、バグ トリアージの実行方法。
記事を読む →データベースとSQLSQL サブクエリのチュートリアル: 型、相関サブクエリ、および EXISTS と例
実行したクエリを含む SQL サブクエリのチュートリアル: スカラー、IN、FROM、SELECT サブクエリ、相関サブクエリ、EXISTS、NOT IN NULL トラップ、サブクエリ対 JOIN 対 CTE。
記事を読む →データベースとSQLSQL トランザクションと ACID の説明: COMMIT、ROLLBACK、分離レベル
SQL トランザクションと ACID について、実行した銀行振込で説明しました。COMMIT、ROLLBACK、セーブポイント、ロック、分離レベル、ダーティ リード、デッドロック、ベスト プラクティスについて説明します。
記事を読む →ソフトウェア開発Git mergeとrebaseの違い:例と使い分け
Git mergeとrebaseの違いを、実際のログ出力、比較表、rebaseの黄金律、安全なチームワークフローで解説します。
記事を読む →ソフトウェア開発Pythonのリスト内包表記:構文・例・使うべきでない場面
Pythonのリスト内包表記を解説:構文、フィルタ、if-else、ネストしたループ、実測した処理時間、そして通常のforループの方が良い場面。
記事を読む →データベースとSQLSQL JOINを例で解説(Inner・Left・Right・Full)
サンプルのテーブルとクエリでSQL JOINを順に解説。INNER、LEFT、RIGHT、FULL、CROSS、SELFに加え、よくある間違いとパフォーマンスのコツも紹介します。
記事を読む →