Snowflake「Single-row subquery returns more than one row」エラーの原因と修正方法

SnowflakeのSingle-row subquery returns more than one rowエラー: エラーの原因と修正方法 Snowflake
この記事をシェアする𝕏B!FacebookLINEPocket

はじめに:「サブクエリが1行を返すはずなのに複数行返ってきたよ!」と怒られる話

こんにちは!Snowflakeで SELECT 文や UPDATE 文を書いているときに、こんなエラーメッセージに出会ったことはありませんか?

Single-row subquery returns more than one row.

直訳すると「単一行を返すはずのサブクエリが、複数行を返してしまったよ」というメッセージです。SQLを書き始めたばかりの方が一度はぶつかる、いわば「サブクエリの登竜門エラー」とも言える存在ですね。

この記事では、なぜこのエラーが出るのか、そしてどう修正すれば安全に解決できるのかを、初心者の方にもわかるようにやさしく解説していきます。一緒に見ていきましょう!

アイキャッチ図解。Snowflakeで発生する「Single-row subquery returns more than one row」エラーの概要を示し、スカラーサブクエリが複数行を返してしまう原因と修正方針を視覚的に整理したイメージ

エラーの正体:なぜ「1行だけ」を期待されているの?

SQLのサブクエリには、「スカラーサブクエリ」と呼ばれる種類があります。これは「値をひとつだけ返すサブクエリ」のことで、=, <, > などの比較演算子の右側に書いたり、SELECT句の中に直接埋め込んだりするタイプです。

例えば、こんなクエリを書いたとしましょう。

SELECT *
FROM orders
WHERE customer_id = (
  SELECT customer_id
  FROM customers
  WHERE region = 'TOKYO'
);

このとき、Snowflakeは「= の右側は値ひとつだよね?」と思っています。でも、東京にいる顧客が複数いる場合、サブクエリは複数行を返してきます。そこで「1個を期待してたのに複数来たよ!」とエラーになるわけですね。

修正方法1:複数行を許容する IN 句に変える

そもそも「複数の顧客を対象にしたい」のなら、 = ではなく IN を使うのが正解です。

SELECT *
FROM orders
WHERE customer_id IN (
  SELECT customer_id
  FROM customers
  WHERE region = 'TOKYO'
);

IN は「リストの中のいずれかに一致するか」を判定する演算子なので、サブクエリが何行返してきても問題なく動きます。ロジック的に「該当する全員」を取りたいケースでは、これが一番自然な書き方ですね。

修正方法2:MAX や MIN で1行に集約する

「最新の1件だけ」「最大金額の1件だけ」を取りたい場面では、集約関数を使ってサブクエリを強制的に1行にしてしまう方法が有効です。

SELECT *
FROM orders
WHERE order_date = (
  SELECT MAX(order_date)
  FROM orders
  WHERE customer_id = 100
);

MAX(), MIN(), COUNT(), SUM() といった集約関数は必ず1行を返してくれるので、スカラーサブクエリとして安心して使えます。「最新日付のレコードだけ抽出したい」みたいなユースケースで頻出のパターンです。

図解:Snowflakeのスカラーサブクエリで複数行エラーを回避するため、MAXやMIN等の集約関数を使ってサブクエリの戻り値を強制的に1行に集約し、WHERE句で安全に比較できる仕組みを示すイメージ

修正方法3:相関サブクエリで行ごとに1件に絞る

「顧客ごとの最新注文を取りたい」のように、外側のテーブルの値ごとに1行ずつ評価したい場面では「相関サブクエリ」が便利です。サブクエリの中で外側のテーブルの列を参照する書き方ですね。

SELECT o.*
FROM orders o
WHERE o.order_date = (
  SELECT MAX(o2.order_date)
  FROM orders o2
  WHERE o2.customer_id = o.customer_id
);

外側の o.customer_id をサブクエリ内で参照することで、顧客ごとに最大日付を計算し、その行だけを残せます。QUALIFY と ROW_NUMBER() を使うのもオシャレな書き方ですよ。

SELECT *
FROM orders
QUALIFY ROW_NUMBER() OVER (
  PARTITION BY customer_id ORDER BY order_date DESC
) = 1;

つまずきポイントと注意点

  • UPDATE 文の SET 句にサブクエリを書くときも要注意。1顧客に複数のレコードが紐づくと同じエラーになります。
  • 「絶対に1行しか返らないはず」と思っていても、データの増加で突然エラーになることがあります。本番運用では MAX や LIMIT 1 で防御的に書くのがおすすめです。
  • このエラーは構文エラーではなく実行時エラーなので、テストデータでは出ず本番で初めて出ることも。日付フォーマットのトラブル(Date is not recognizedエラー)や識別子トラブル(Invalid identifierエラー)と同じく、SQLを書く際の典型的な落とし穴です。

まとめ

「Single-row subquery returns more than one row」エラーは、= や < の右側に置いたサブクエリが2行以上返してしまうことで発生します。修正の選択肢は主に3つです。

  • 複数行に該当させたいなら IN 句に変える
  • 1行に絞り込みたいなら MAX/MIN などの集約関数で囲む
  • 外側の行ごとに1件評価したいなら 相関サブクエリや QUALIFY + ROW_NUMBER() を使う

サブクエリの「何行返ってくるか」を意識する癖がつくと、SQLが一気に書きやすくなります。エラーに出会ったときこそスキルアップのチャンスです、ぜひ恐れずに対処していきましょう!

参考リンク

関連記事

この記事をシェアする𝕏B!FacebookLINEPocket