MySQLエラー 1242 (21000) の解決:Subquery returns more than 1 row の対処法

intermediate🗄️ MySQL2026-07-18| MySQL 5.7, MySQL 8.0+, MariaDB (Ubuntu, CentOS, Windows, macOS)

Error Message

ERROR 1242 (21000): Subquery returns more than 1 row
#mysql#データベース#sql最適化#トラブルシューティング

The Error Message

複雑なレポートの実行中やレコードの更新中に、MySQL が突然停止することがあります。ターミナルやアプリケーションのログには、次のメッセージが表示されます:

ERROR 1242 (21000): Subquery returns more than 1 row

Why This Happens

本質的に、これは logic mismatch です。MySQL は scalar value5 という数値や 'Active' という文字列のような単一のデータ)を期待していますが、subquery がリストを返しています。これは通常、以下の 3 つの特定のシナリオで発生します:

  • 1 つの値でしか機能しない comparison operator(=<>)を使用した。
  • 単一の列を作成するために、SELECT list 内に subquery が配置されている。
  • subquery が、arrays や sets をサポートしていない関数の argument として機能している。

データベースの logic が「ユーザーは 1 つの active subscription しか持たない」と想定しているのに、実際のデータに 3 つ含まれている場合、query は即座にエラーになります。

Step-by-Step Fixes

Method 1: Use the LIMIT 1 Clause

最新の record や特定の代表値が 1 つだけ必要な場合は、LIMIT 1 が最も直接的な解決策です。これにより、subquery は最初の一致を見つけた時点で停止し、scalar requirement を満たします。

失敗する query の例:

SELECT name, 
       (SELECT order_date FROM orders WHERE orders.user_id = users.id) as last_order
FROM users;

ユーザー #402 が 12 件の orders を出している場合、subquery は 12 個の dates を返し、query はクラッシュします。修正方法:

SELECT name, 
       (SELECT order_date FROM orders 
        WHERE orders.user_id = users.id 
        ORDER BY order_date DESC LIMIT 1) as last_order
FROM users;

Method 2: Switch from = to IN

equals operator(=)は厳密であり、単一の一致を要求します。subquery が IDs の collection を返す可能性がある場合は、等号を IN operator に置き換えてください。IN は、値のリストを処理するために特別に設計されています。

失敗する query の例:

SELECT * FROM products 
WHERE category_id = (SELECT id FROM categories WHERE name = 'Electronics');

店舗に 'Retail' と 'Wholesale' の両方の 'Electronics' categories(IDs 5 と 12)がある場合、subquery は両方を返します。修正方法:

SELECT * FROM products 
WHERE category_id IN (SELECT id FROM categories WHERE name = 'Electronics');

Method 3: Use Aggregate Functions

ランダムな row ではなく、summary が必要な場合があります。MAX()MIN()SUM() などの aggregate functions は、複数の rows を自然に 1 つの scalar value に圧縮します。これは、財務データや時系列データに最適です。

失敗する query の例:

UPDATE employees 
SET salary = (SELECT amount FROM bonuses WHERE employee_id = 101)
WHERE id = 101;

従業員 101 が今月 3 つの異なる bonuses を受け取った場合、update は失敗します。修正方法(SUM を使用):

UPDATE employees 
SET salary = salary + (SELECT SUM(amount) FROM bonuses WHERE employee_id = 101)
WHERE id = 101;

Method 4: Use EXISTS for Presence Checks

関連する record が存在するかどうかだけを確認する場合は、subquery の比較を完全に避けてください。代わりに EXISTS を使用します。一致するものが 1 つ見つかるとすぐに検索を停止するため、パフォーマンスが向上します。また、データではなく boolean(true/false)を返すため、row count errors を回避できます。

失敗する query の例:

SELECT * FROM articles 
WHERE id = (SELECT article_id FROM comments WHERE status = 'pending');

修正方法:

SELECT * FROM articles a 
WHERE EXISTS (SELECT 1 FROM comments c WHERE c.article_id = a.id AND c.status = 'pending');

Method 5: Audit for Duplicate Data

SQL が完璧でも、データが整理されていないことがあります。一意であるべきテーブル(user_profiles など)に同じ user_id の rows が 2 つ含まれている場合、Error 1242 が発生します。これは多くの場合、database constraint が欠落している兆候です。

次のパターンを使用して重複を検索します:

SELECT user_id, COUNT(*) 
FROM profiles 
GROUP BY user_id 
HAVING COUNT(*) > 1;

重複をクリーンアップした後、その列に UNIQUE constraint を適用して、将来エラーが再発するのを防ぎます。

Verification

デプロイする前に、MySQL Workbench や TablePlus などの安全な環境で修正をテストしてください。次の 3 点を確認します:

  • エラーメッセージが表示されなくなったことを確認する。
  • row count を確認する。subquery の範囲が広すぎる場合、IN を使用すると意図したよりも多くの rows が返されることがあります。
  • LIMIT 1 を使用した場合は、ORDER BY logic を再確認する。単にランダムな record ではなく、「最新」や「最安」の record が取得されていることを確認してください。

Pro Tips for Prevention

  • デフォルトで LIMIT 1 を使用する: scalar subqueries では、LIMIT 1 を追加することで、予期しないデータ増加に対するセーフティネットとして機能します。
  • JOIN を選択する: 可能な限り、LEFT JOIN または INNER JOIN を使用してください。これらは一般的に高速で、nested subqueries よりもデバッグがはるかに簡単です。
  • Enforce Uniqueness: 1対1の関係を表す列には UNIQUE indexes を使用します。これにより、エラーの発生源であるデータ入力時点でエラーを阻止できます。
  • Scale Testing: 5 行のテストデータで動作する query でも、50,000 行の本番データでは壊れる可能性があります。常に現実的な datasets でテストしてください。

Related Error Notes