yieldを含む関数は ジェネレーター を返し、その本体は値を要求するまで実行されません。yield関数を一時停止し、次のリクエストまでローカル状態を保持します。- ジェネレーターは、生成する値が多くても、少量の固定量のメモリーを使用します。
- ジェネレーターは 1 回しか消費できません。これが最も一般的なトラップです。
エラーを見つけるためだけに 10 GB のログ ファイルをリストにロードすることを想像してください。プログラムがメモリを見つける前にメモリ不足になる可能性があります。 Python ジェネレーター は、オンデマンドで一度に 1 つずつ値を生成することでこの問題を解決し、メモリをほとんど使わずに巨大なデータ、さらには無限のデータを処理できるようにします。
このガイドでは、ジェネレーターの実行時に実際に何が起こるかを段階的に説明します。各例を Python 3.13 で実行し、その実行からの出力が得られます。 初心者向けの Python チュートリアルで説明されている関数とループにすでに慣れていると役立ちます。
発電機とは何ですか?
ジェネレーターは、yield を使用して関数を作成することで作成する特別な種類のイテレーターです。 yield が含まれる関数は自動的にジェネレーター関数になり、それを呼び出すと、コードを実行するのではなく、ジェネレーター オブジェクトが得られます。
def count_up(limit):
print("starting")
n = 1
while n <= limit:
yield n
n += 1
print("finished")
gen = count_up(2)
print(gen)
# <generator object count_up at 0x...>
「starting」が出力されないことに注意してください。関数を呼び出すとジェネレーターのみが作成されます。内部ではまだ何も実行されていません。
歩留まりのしくみ、ステップバイステップ
next() を呼び出すたびに、yield に到達するまで関数が実行され、その値が返され、その場でフリーズします。次の呼び出しは、すべてのローカル変数をそのままにして、その行の直後から再開されます。
print(next(gen))
# starting
# 1
print(next(gen))
# 2
next(gen)
# finished
# StopIteration
出力に従ってください。 「starting」は最初のnext()にのみ表示されます。 2 番目では、停止した場所から実行が続行されるため、n はまだ利用可能で 2 になります。 3 番目では、ループが終了し、「finished」が出力され、関数が最後から落ち、Python は StopIteration を上げて、何も残っていないことを示します。
next() を自分で呼び出すことはほとんどありません。 for ループはこれを実行し、StopIteration を静かに処理します。また、list()、sum()、および同様の関数も同様に処理します。
print(list(count_up(3)))
# starting
# finished
# [1, 2, 3]
利回りとリターン
| アスペクト | 戻る |
収量 |
|---|---|---|
| 何をするのか | 機能を終了します | 機能を一時停止します |
| 生み出される価値 | 1 つの結果 | シリーズ、一度に 1 つずつ |
| ローカル状態 | 廃棄されました | 値の間で保持される |
| コードが実行されるとき | 呼ばれたらすぐに | 値が要求された場合のみ |
| 多くの値の記憶 | それらをすべて保持する必要があります | 一度に1つずつ保持します |
この遅延は 遅延評価 として知られており、値は必要な場合にのみ計算されます。ジェネレーター内のプレーン return は単にジェネレーターを終了し、StopIteration をトリガーします。
測定されたメモリの違い
sys.getsizeof を使用して、100 万正方形のリストを同等のジェネレーターと比較しました。
import sys
squares_list = [n * n for n in range(1_000_000)]
squares_gen = (n * n for n in range(1_000_000))
print(sys.getsizeof(squares_list), sys.getsizeof(squares_gen))
# 8448728 200
リスト自体の構造には約 8.4 MB が必要でしたが、システム上ではジェネレーターに必要なバイト数はわずか 200 バイトでした。 getsizeof ではリスト内の整数オブジェクトがカウントされないため、実際のギャップはさらに大きくなります。ジェネレーターのサイズは、結果ではなく現在の状態のみを保存するため、10 億個のアイテムに対して同じままです。
ただし、トレードオフもあります。 len() を使用すると、リストを再利用したり、インデックスを作成したり、測定したりできます。ジェネレーターはこれらのどれも提供しません。ランダム アクセスが必要な場合、または少量のデータを数回ループする場合は、リストの方が簡単です。これらの型の詳細については、Python リスト、タプル、セット のガイドを参照してください。
ジェネレータ式
単純な場合には関数は必要ありません。ジェネレーター式は、角括弧の代わりに丸括弧を使用した リスト内包表記 のように見えます。
squares = [n * n for n in range(5)] # builds a list now
squares = (n * n for n in range(5)) # produces values on demand
print(sum(n * n for n in range(5)))
# 30
sum() のように、ジェネレーター式が関数の唯一の引数である場合は、余分な括弧を省略できます。一時的なリストが作成されないため、これは大規模なデータの合計を計算する場合に適切な習慣です。
データパイプラインを構築する
ここで発電機が真価を発揮します。小さなジェネレーターをチェーンして、各アイテムが一度に 1 つずつすべてのステップを通過するようにすることができます。
def read_lines(lines):
for line in lines:
yield line.strip()
def only_errors(lines):
for line in lines:
if line.startswith("ERROR"):
yield line
def messages(lines):
for line in lines:
yield line.split(" ", 1)[1]
log = ["INFO started\n", "ERROR db down\n",
"ERROR disk full\n", "INFO stopped\n"]
print(list(messages(only_errors(read_lines(log)))))
# ['db down', 'disk full']
各ステップは 1 つの小さなジョブを実行し、単独でテストできます。 Python ファイル処理のガイド で示されているように、ファイル オブジェクトは 1 行ずつ読み取ることができるため、実際に使用する場合は、開いているファイルのサンプル リストを交換します。数ギガバイトのログであっても、非常に少ないメモリで処理されます。
ヒント: パイプラインは、何かが消費するまで機能しません。チェーンの構築は瞬時に行われます。この処理は、最終ジェネレーターをループするか、list() のような呼び出しでラップする場合にのみ発生します。
無限シーケンス
リストは無限にすることはできませんが、ジェネレーターは次の値のみを計算するため、無限にすることができます。 itertools.islice を使用して必要な数だけ取得します。
import itertools
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
print(list(itertools.islice(fibonacci(), 8)))
# [0, 1, 1, 2, 3, 5, 8, 13]
ただし、注意してください。制限なしで list(fibonacci()) を呼び出すと、メモリがなくなるまで実行されます。同じパターンは、ID ジェネレーター、ポーリング ループ、ページ分割された API 結果にも適しており、現在のページがなくなった場合にのみ次のページをフェッチします。
およびバッチからの収量
yield from 別の反復可能オブジェクトにハンドオフし、ネストされたループを節約します。
def chain_all(*groups):
for group in groups:
yield from group
print(list(chain_all([1, 2], (3, 4))))
# [1, 2, 3, 4]
非常に実用的なパターンは、データを固定サイズのバッチに分割することです。たとえば、一度に 1,000 行をデータベースに挿入します。
def batches(items, size):
batch = []
for item in items:
batch.append(item)
if len(batch) == size:
yield batch
batch = []
if batch:
yield batch
print(list(batches(range(7), 3)))
# [[0, 1, 2], [3, 4, 5], [6]]
最後の if batch は、残った小さいグループが失われないようにします。 Python 3.12 以降にも、同様の働きをする itertools.batched が含まれています。
送信、クローズ、クリーンアップ
ジェネレーターは値を受け取ることもできます。 send() はジェネレーターを再開し、値は yield の式の結果になります。最初の yield に到達するには、最初に next() を 1 回呼び出す必要があります。
def running_total():
total = 0
while True:
value = yield total
total += value
t = running_total()
next(t)
print(t.send(10), t.send(5))
# 10 15
ほとんどの人はこれを必要とすることはほとんどありません。さらに一般的な懸念はクリーンアップです。早めに停止した場合、ジェネレーターが閉じているときでも finally ブロックが実行されます。
def tracked():
try:
yield 1
yield 2
finally:
print("cleanup ran")
tr = tracked()
next(tr)
tr.close()
# cleanup ran
Pytest はまさにこのアイデアを使用しています。pytest チュートリアル が示すように、yield のフィクスチャはその後クリーンアップを実行します。同様に、標準ライブラリの contextmanager デコレータはジェネレータを with ブロックに変換します。
クラス内のジェネレーター
__iter__ メソッドをジェネレーターとして記述するのが、独自のクラスを反復可能にする最も簡単な方法です。ボーナスとして、各ループで新しいジェネレーターが取得されるため、オブジェクトを何度もループすることができます。
class Countdown:
def __init__(self, start):
self.start = start
def __iter__(self):
n = self.start
while n > 0:
yield n
n -= 1
c = Countdown(3)
print(list(c), list(c))
# [3, 2, 1] [3, 2, 1]
発電機を使用する場合
| こんなときは発電機を使いましょう | リストを使用する場合 |
|---|---|
| データが大きいか、サイズが不明です | データが小さくメモリに収まる |
| アイテムを順番に一度処理します | 何度かループします |
| 早めにやめてもいいよ | len()、インデックスまたはスライスが必要です |
| ストリームまたはファイルを読んでいます | データを並べ替えるか逆順にする必要があります |
| パイプラインを構築しています | 記憶よりもシンプルさが重要 |
ジェネレーターは全体的に必ずしも高速であるとは限りません。最終的にすべての値を使用する場合でも、合計の作業量は同様になり、項目ごとのオーバーヘッドが少なくなるため、小さなデータの場合はリストの理解がさらに速くなります。主な利点は、メモリを使用し、最初の結果をより早く取得できることです。
よくある間違い
使い終わった発電機を再利用する
これは、ある時点でほぼすべての人に影響を及ぼします。ジェネレーターは完了したことを記憶します。
g = count_up(2)
print(list(g), list(g))
# [1, 2] []
2 番目の呼び出しでは、エラーがまったくない空のリストが取得されます。関数を再度呼び出して新しいジェネレーターを取得するか、上記のクラス アプローチを使用します。
コードがすぐに実行されることを期待する
def noisy():
print("body runs")
yield 1
n = noisy() # nothing is printed
print(next(n)) # body runs, then 1
ジェネレーターの先頭での引数チェックは最初の next() まで実行されないため、エラーは、その原因となった呼び出しから遠く離れたところで発生する可能性があります。
その他のトラップ
len()の呼び出しまたはインデックス作成。 ジェネレータはどちらもサポートせず、TypeErrorを生成します。- ファイルを閉じるのが早すぎます。
withブロック内からジェネレーターを返すと、行が読み取られる前にファイルが閉じられる可能性があります。withブロックの中にyieldを入れます。 - すべてを
list()でラップします。 これにより、メモリの利点が失われます。 - 小さなデータにはこれを使用します。 多くの場合、単純なリストの方が明確です。
まとめ
ジェネレーターは一時停止できる機能です。この 1 つのアイデアにより、メモリ使用量が低くなり、無限のシーケンスとクリーンなパイプラインが実現します。これを実践するには、一度ループするだけの大きなリストを構築するコード内の場所を見つけて、それをジェネレーターに変換します。次に、実際のログ ファイル上で 2 つの小さなジェネレーターをチェーンしてみてください。
よくある質問
Python で yield は何をしますか?
yield は値を生成し、ローカル変数を保持したまま関数を一時停止します。次の値が要求されると、関数は同じポイントから再開します。
ジェネレーターとリストの違いは何ですか?
リストはすべての値を一度にメモリに保存します。ジェネレーターはオンデマンドで各値を計算し、メモリをほとんど使用せず、通常は 1 回だけ使用できます。
イテレータとジェネレータの違いは何ですか?
すべてのジェネレータはイテレータです。ジェネレーターは、Python が __iter__ メソッドと __next__ メソッドを作成するため、ジェネレーターを作成する最も簡単な方法です。
ジェネレーターを 2 回ループできますか?
いいえ。使い果たされると、エラーが発生することなく、それ以上何も生成されません。関数を再度呼び出して新しいジェネレーターを作成するか、結果をリストに保存します。
ジェネレーターはリストよりも高速ですか?
いつもではありません。使用するメモリがはるかに少なく、最初の結果がすぐに得られますが、少量のデータを完全に消費する場合、リストの理解がわずかに速くなります。
学び続ける
テスト、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に加え、よくある間違いとパフォーマンスのコツも紹介します。
記事を読む →