【MariaDB】サブクエリとEXISTSのA to Z:エラー解決とパフォーマンス最適化

2025-05-26

MariaDB(MySQLと高い互換性を持つRDBMS)において、サブクエリとEXISTSは、複雑な条件でのデータ抽出や、複数のテーブル間の関連性をチェックする際に非常に強力なツールです。

サブクエリ(Subquery)とは

サブクエリとは、別のSQL文(外部クエリ)の中にネストされた(入れ子になった)SQLクエリのことです。サブクエリは、外部クエリが必要とする値や行セットを提供するために使用されます。

基本的な考え方

  • サブクエリが返す結果によって、様々な使い方が可能です。
    • 単一の値(スカラサブクエリ)
    • 単一の列の複数の値(IN句やANY/ALL句と組み合わせて使用)
    • 複数の列の複数の行(導出テーブルとしてFROM句で使用)
    • 行の存在チェック(EXISTS句と組み合わせて使用)
  • サブクエリはまず単独で実行され、その結果が外部クエリに渡されます。
  • サブクエリは括弧 () で囲まれます。


ordersテーブルに注文が存在する顧客の情報をcustomersテーブルから取得する」という場合、以下のように書けます。

SELECT customer_name
FROM customers
WHERE customer_id IN (SELECT customer_id FROM orders);

この例では、SELECT customer_id FROM orders がサブクエリであり、ordersテーブルに存在する顧客IDのリストを返します。外部クエリはそのリストに含まれる顧客IDを持つ顧客の名前を抽出します。

EXISTSオペレータとは

EXISTSオペレータは、サブクエリが1つでも行を返す場合にTRUE(真)を返し、行を全く返さない場合にFALSE(偽)を返す論理オペレータです。WHERE句で条件として使用されます。

EXISTSの最も重要な特徴は、サブクエリが実際に返すデータの内容(カラムの値)には関心がなく、単に行が返されるかどうかにのみ関心がある点です。そのため、SELECT *SELECT 1 のように指定しても結果は同じになります。

構文

SELECT カラム1, カラム2, ...
FROM テーブル名
WHERE EXISTS (サブクエリ);

EXISTSの挙動と利点

  • パフォーマンス
    大量のデータが存在する場合、IN句よりもEXISTSの方が高速になることがあります。IN句はサブクエリの結果セットを完全に生成してから外部クエリと比較するのに対し、EXISTSはサブクエリが1行でも見つかった時点で処理を中断できるためです。ただし、これはインデックスの有無やクエリオプティマイザの判断によって異なります。
  • 相関サブクエリとの相性
    EXISTSは「相関サブクエリ」と組み合わせて使用されることが非常に多いです。相関サブクエリとは、外部クエリの各行に対してサブクエリが実行され、サブクエリの条件に外部クエリのカラムが使用されるものです。
  • 存在チェックに特化
    特定の条件を満たすデータが存在するかどうかを効率的にチェックします。


先ほどの例をEXISTSを使って書き直してみます。

ordersテーブルに注文が存在する顧客の情報をcustomersテーブルから取得する」

SELECT c.customer_name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

解説

  1. 外部クエリがcustomersテーブルから1行ずつ顧客データを取り出します(例えば、c.customer_id = 1の顧客)。
  2. その顧客ID (c.customer_id) を使って、サブクエリ (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id) が実行されます。
  3. もしordersテーブルにc.customer_idと同じcustomer_idを持つ注文が1つでも存在すれば、サブクエリは1行以上の結果を返します。
  4. この場合、EXISTS条件はTRUEとなり、外部クエリはその顧客 (c.customer_name) を結果セットに含めます。
  5. もし一致する注文が1つもなければ、サブクエリは何も行を返さず、EXISTS条件はFALSEとなり、その顧客は結果セットに含まれません。

NOT EXISTSオペレータ

EXISTSの逆の条件をチェックするのがNOT EXISTSです。サブクエリが行を全く返さない場合にTRUEを返し、1つでも行を返す場合にFALSEを返します


ordersテーブルにまだ注文が存在しない顧客の情報をcustomersテーブルから取得する」

SELECT c.customer_name
FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);
  • NOT EXISTS
    EXISTSの逆で、サブクエリが全く結果を返さない場合にTRUEを返す。
  • EXISTS
    サブクエリが1行でも結果を返せばTRUE、そうでなければFALSEを返す論理オペレータ。主に相関サブクエリと組み合わせて、関連するデータの存在チェックに用いられる。データ内容ではなく「存在するかどうか」のみを判断するため、効率的なクエリが可能。
  • サブクエリ
    SQL文の中にネストされた別のSQL文。外部クエリに必要なデータを提供する。


エラー:サブクエリが複数の行を返す場合 (Subquery returns more than 1 row)

エラーコード
ERROR 1242 (21000): Subquery returns more than 1 row

原因
スカラサブクエリ(単一の値を返すことが期待されるサブクエリ)が、実際には複数の行を返してしまった場合に発生します。例えば、=(イコール)演算子や、単一の行を期待するコンテキストでサブクエリを使用した場合などです。


-- customersテーブルにcustomer_idが重複している場合に発生する可能性がある
SELECT customer_name
FROM customers
WHERE customer_id = (SELECT customer_id FROM orders WHERE order_total > 100);

もしordersテーブルでorder_total > 100となるcustomer_idが複数存在する場合、このクエリはエラーになります。

トラブルシューティング

  • サブクエリを制限する
    LIMIT 1などを使って、サブクエリが返す行数を意図的に1つに制限する方法もありますが、結果が複数ある場合にどの1行が選ばれるかは保証されないため、慎重に検討する必要があります。

    -- 意図的に単一の行を返すように制限する場合
    SELECT customer_name
    FROM customers
    WHERE customer_id = (SELECT customer_id FROM orders WHERE order_total > 100 ORDER BY order_date DESC LIMIT 1);
    
  • EXISTSを使用する
    単に存在チェックをしたいだけで、サブクエリが返す具体的な値には興味がない場合は、EXISTSを使用します。

    SELECT c.customer_name
    FROM customers c
    WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.order_total > 100);
    
  • INまたはANY/ALLを使用する
    サブクエリが複数の値を返す可能性がある場合は、=の代わりにINANYALLを使用します。

    • IN: サブクエリが返す値のいずれかに一致する場合
    • ANY / SOME: サブクエリが返す値のいずれかが条件を満たす場合
    • ALL: サブクエリが返す全ての値が条件を満たす場合
    SELECT customer_name
    FROM customers
    WHERE customer_id IN (SELECT customer_id FROM orders WHERE order_total > 100);
    

エラー:不正なカラム数 (Operand should contain 1 column(s))

エラーコード
ERROR 1241 (21000): Operand should contain 1 column(s)

原因
サブクエリが単一のカラムを返すことが期待されるコンテキストで、複数のカラムを返してしまった場合に発生します。


SELECT customer_name
FROM customers
WHERE (customer_id, customer_status) = (SELECT customer_id, status FROM orders WHERE order_id = 123);

トラブルシューティング

  • 行コンパリゾンを正しく使用する
    複数のカラムを比較したい場合は、行コンパリゾンの構文が適用されるか確認します。

    -- 正しい行コンパリゾン(MariaDB/MySQLではサポートされているが、すべてのRDBMSで一般的ではない)
    SELECT customer_name
    FROM customers
    WHERE (customer_id, customer_status) IN ((SELECT customer_id, status FROM orders WHERE order_id = 123));
    

    または、個々のカラムで比較するように分解します。

    SELECT c.customer_name
    FROM customers c
    WHERE c.customer_id = (SELECT o.customer_id FROM orders o WHERE o.order_id = 123)
      AND c.customer_status = (SELECT o.status FROM orders o WHERE o.order_id = 123);
    
  • 単一のカラムのみを選択する
    サブクエリが返すカラムが必要なカラムのみであることを確認します。

エラー:同じテーブルの更新と参照 (You can't specify target table 'x' for update in FROM clause)

エラーコード
ERROR 1093 (HY000): Table 'x' is specified twice, both as a target for 'UPDATE' and as a separate source for data

原因
UPDATEまたはDELETEステートメントのサブクエリ内で、更新/削除対象のテーブルと同じテーブルを参照している場合に発生します。これは、データの整合性を保つためのMariaDB/MySQLの制限です。


-- 同じテーブルを更新しながら参照しようとしている
UPDATE products
SET price = price * 1.1
WHERE product_id IN (SELECT product_id FROM products WHERE stock_quantity < 10);

トラブルシューティング

  • JOINを使用する
    多くのサブクエリはJOIN句に書き換えることができ、その方がパフォーマンスが良い場合が多いです。

    UPDATE products p
    JOIN (SELECT product_id FROM products WHERE stock_quantity < 10) AS low_stock
    ON p.product_id = low_stock.product_id
    SET p.price = p.price * 1.1;
    
  • 導出テーブル(Derived Table)を使用する
    サブクエリをFROM句で導出テーブルとして使用することで、この制限を回避できます。

    UPDATE products p
    SET p.price = p.price * 1.1
    WHERE p.product_id IN (
        SELECT temp.product_id
        FROM (SELECT product_id FROM products WHERE stock_quantity < 10) AS temp
    );
    

パフォーマンスに関する問題

サブクエリやEXISTSが遅いと感じる場合、以下の点を確認し、最適化を検討してください。

原因



まず、例で使用するテーブルを作成し、データを投入します。

-- 顧客テーブル
CREATE TABLE customers (
    customer_id INT PRIMARY KEY AUTO_INCREMENT,
    customer_name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE,
    registration_date DATE
);

-- 商品テーブル
CREATE TABLE products (
    product_id INT PRIMARY KEY AUTO_INCREMENT,
    product_name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    stock_quantity INT NOT NULL
);

-- 注文テーブル
CREATE TABLE orders (
    order_id INT PRIMARY KEY AUTO_INCREMENT,
    customer_id INT NOT NULL,
    order_date DATE NOT NULL,
    total_amount DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

-- 注文詳細テーブル (どの商品が注文されたか)
CREATE TABLE order_items (
    order_item_id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    item_price DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

-- データ挿入
INSERT INTO customers (customer_name, email, registration_date) VALUES
('佐藤 太郎', '[email protected]', '2023-01-10'),
('鈴木 花子', '[email protected]', '2023-02-15'),
('田中 次郎', '[email protected]', '2023-03-20'),
('山本 明美', '[email protected]', '2023-04-01'),
('中村 健太', '[email protected]', '2023-05-05');

INSERT INTO products (product_name, price, stock_quantity) VALUES
('ノートPC', 120000.00, 50),
('スマートフォン', 80000.00, 120),
('ワイヤレスイヤホン', 15000.00, 200),
('モバイルバッテリー', 3000.00, 300),
('キーボード', 8000.00, 80);

INSERT INTO orders (customer_id, order_date, total_amount) VALUES
(1, '2024-01-20', 120000.00), -- 佐藤太郎: ノートPC
(2, '2024-02-25', 80000.00),  -- 鈴木花子: スマートフォン
(1, '2024-03-01', 15000.00),  -- 佐藤太郎: ワイヤレスイヤホン
(3, '2024-04-10', 3000.00),   -- 田中次郎: モバイルバッテリー
(2, '2024-05-15', 8000.00);   -- 鈴木花子: キーボード

INSERT INTO order_items (order_id, product_id, quantity, item_price) VALUES
(1, 1, 1, 120000.00),
(2, 2, 1, 80000.00),
(3, 3, 1, 15000.00),
(4, 4, 1, 3000.00),
(5, 5, 1, 8000.00);

-- 注文がない顧客を確認するためのNULLデータ
-- (山本明美と中村健太はまだ注文がない状態)

スカラサブクエリ (Scalar Subquery)

単一の値を返すサブクエリで、主にWHERE句やSELECT句で使用されます。

例1-1: 平均価格より高い商品を探す

SELECT product_name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);

解説
SELECT AVG(price) FROM products が最初に実行され、商品の平均価格(単一の値)を返します。外部クエリはその平均価格より高い商品を選択します。

IN句とサブクエリ

サブクエリが複数の値を返す場合に、外部クエリの条件にそのリストを使用します。

例2-1: 少なくとも1つ注文したことがある顧客の名前を取得する

SELECT customer_name
FROM customers
WHERE customer_id IN (SELECT customer_id FROM orders);

解説
SELECT customer_id FROM orders が実行され、注文したことのある顧客IDのリストを返します。外部クエリはそのリストに含まれるcustomer_idを持つ顧客の名前を取得します。

例2-2: 在庫が100個以下の商品を含む注文の詳細を取得する

SELECT oi.order_id, p.product_name, oi.quantity
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
WHERE oi.product_id IN (SELECT product_id FROM products WHERE stock_quantity < 100);

解説
まず、stock_quantity < 100 の条件を満たすproduct_idのリストがサブクエリで取得されます。次に、order_itemsproductsを結合し、そのリストに含まれるproduct_idを持つ注文アイテムをフィルタリングします。

EXISTSと相関サブクエリ

EXISTSは、サブクエリが1つでも行を返すかどうかの真偽値に基づいて外部クエリの行をフィルタリングします。通常、外部クエリの各行に対してサブクエリが実行される「相関サブクエリ」として使用されます。

例3-1: 少なくとも1つ注文したことがある顧客の名前を取得する(EXISTS版)

SELECT c.customer_name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

解説
customersテーブルの各行(例えば、c.customer_id = 1の顧客)に対して、サブクエリSELECT 1 FROM orders o WHERE o.customer_id = c.customer_idが実行されます。もしordersテーブルにその顧客IDの注文が1つでもあれば、EXISTSTRUEを返し、その顧客のcustomer_nameが結果に含まれます。

例3-2: 特定の商品(例: 'ノートPC')を購入した顧客の名前を取得する

SELECT c.customer_name
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    JOIN order_items oi ON o.order_id = oi.order_id
    JOIN products p ON oi.product_id = p.product_id
    WHERE o.customer_id = c.customer_id
    AND p.product_name = 'ノートPC'
);

解説
これはより複雑なEXISTSの例です。各顧客に対して、その顧客が'ノートPC'を含む注文をしたことがあるかどうかをチェックします。サブクエリ内で複数のテーブルを結合して条件を絞り込んでいます。

NOT EXISTSと相関サブクエリ

NOT EXISTSEXISTSの逆で、サブクエリが全く行を返さない場合にTRUEを返します。

例4-1: まだ一度も注文したことがない顧客の名前を取得する

SELECT c.customer_name
FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

解説
customersテーブルの各行に対して、サブクエリが実行されます。もしordersテーブルにその顧客IDの注文が一つもなければ(サブクエリが何も返さない)、NOT EXISTSTRUEを返し、その顧客の名前が結果に含まれます。この例では、山本明美中村健太が表示されるはずです。

例4-2: 在庫が全くない商品に関連する注文がないことを確認する(例: 注文はあったが、現在は在庫0の商品がない)

SELECT p.product_name, p.stock_quantity
FROM products p
WHERE NOT EXISTS (
    SELECT 1
    FROM order_items oi
    WHERE oi.product_id = p.product_id
    AND p.stock_quantity = 0
);

解説
このクエリは、「在庫が0ではない商品」をリストアップします。もし、ある商品のproduct_idを持つorder_itemsが存在し、かつその商品のstock_quantity0である場合、NOT EXISTSFALSEとなり、その商品は結果に含まれません。

FROM句におけるサブクエリ(導出テーブル)

サブクエリの結果を一時的なテーブルとして扱い、外部クエリでその一時テーブルを結合したり選択したりします。

例5-1: 各顧客の総注文金額と顧客情報を結合する

SELECT c.customer_name, IFNULL(order_summary.total_orders_amount, 0) AS total_spent
FROM customers c
LEFT JOIN (
    SELECT customer_id, SUM(total_amount) AS total_orders_amount
    FROM orders
    GROUP BY customer_id
) AS order_summary ON c.customer_id = order_summary.customer_id;

解説
FROM句内のサブクエリは、各顧客の総注文金額を計算した一時的なテーブルorder_summaryを作成します。その後、customersテーブルとorder_summaryLEFT JOINで結合し、すべての顧客と、存在すればその総注文金額を表示します。IFNULLは、まだ注文がない顧客のtotal_spent0として表示するために使用しています。



主に、JOIN句を使用する方法と、より現代的なCTE(共通テーブル式)を使用する方法が挙げられます。

JOIN句 (JOIN Clauses)

ほとんどのサブクエリ、特にINEXISTSを使用するクエリは、JOIN句に書き換えることが可能です。JOINは、データベースオプティマイザがより効率的な実行計画を立てやすいため、パフォーマンスが改善されることがよくあります。

例1-1: INの代替 - INNER JOIN

元となるサブクエリ (INを使用): 少なくとも1つ注文したことがある顧客の名前を取得

SELECT customer_name
FROM customers
WHERE customer_id IN (SELECT customer_id FROM orders);

INNER JOINへの代替

SELECT c.customer_name
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name;

解説
INNER JOINは、両方のテーブルに一致する行がある場合にのみ結果を返します。GROUP BYを使用することで、同じ顧客が複数の注文を持っていても、顧客名が重複して表示されるのを防ぎます。通常、このJOIN形式はINサブクエリよりもパフォーマンスが良いことが多いです。

例1-2: EXISTSの代替 - INNER JOIN (または EXISTSが適している場合)

元となるサブクエリ (EXISTSを使用): 少なくとも1つ注文したことがある顧客の名前を取得

SELECT c.customer_name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

INNER JOINへの代替

SELECT DISTINCT c.customer_name
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;

解説
この場合もINNER JOINで代用できます。DISTINCTキーワードは、customer_nameが重複して表示されるのを防ぎます。

EXISTS vs JOIN の使い分けについて

  • JOIN
    関連するテーブルからデータを取得したり、集計したりする必要がある場合に適しています。また、オプティマイザがJOIN句の最適化に優れていることが多いため、インデックスが適切であれば高速に実行されます。
  • EXISTS
    サブクエリが1行でも見つかった時点で処理を中断するため、**「存在するかどうか」**だけをチェックする場合には非常に効率的です。特に、外部クエリの行数が少なく、サブクエリの結果セットが非常に大きい場合に有利になることがあります。

どちらが高速かはデータ量やインデックス、MariaDBのバージョンやオプティマイザの挙動に依存するため、EXPLAINで実行計画を確認し、両方の方法でベンチマークを行うことが推奨されます。

例1-3: NOT EXISTSの代替 - LEFT JOIN + IS NULL

元となるサブクエリ (NOT EXISTSを使用): まだ一度も注文したことがない顧客の名前を取得

SELECT c.customer_name
FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

LEFT JOIN + IS NULLへの代替

SELECT c.customer_name
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL; -- ordersテーブルに一致する行がなかった場合、その行はNULLになる

解説
LEFT JOINは、左側のテーブル(customers)のすべての行を保持し、右側のテーブル(orders)から一致する行があればそれを結合します。一致する行がなければ、右側のテーブルのカラムはNULLになります。この性質を利用して、o.order_id IS NULLでフィルタリングすることで、NOT EXISTSと同じ結果を得られます。このパターンは「アンチジョイン」とも呼ばれ、非常に一般的で効率的な代替手段です。

導出テーブル (Derived Tables)

FROM句の中にサブクエリを記述し、そのサブクエリの結果を一時的なテーブル(導出テーブル)として扱います。

例2-1: 各顧客の総注文金額と顧客情報を結合する (FROM句サブクエリ)`

SELECT c.customer_name, IFNULL(order_summary.total_orders_amount, 0) AS total_spent
FROM customers c
LEFT JOIN (
    SELECT customer_id, SUM(total_amount) AS total_orders_amount
    FROM orders
    GROUP BY customer_id
) AS order_summary ON c.customer_id = order_summary.customer_id;

解説
この例は、前回の「プログラミング例」で既に紹介したものですが、これもサブクエリの代替手段として非常に有効です。JOIN句を使用する代わりに、FROM句で複雑な集計やフィルタリングを行った結果を一時的なテーブルとして定義し、それに対して外部クエリを実行します。これにより、クエリの論理構造が明確になり、複雑な計算を段階的に行うことができます。

共通テーブル式 (CTE - Common Table Expressions) / WITH句

MariaDB 10.2以降で利用可能なCTEは、より複雑なクエリをより読みやすく、管理しやすくするための強力な機能です。再帰クエリにも対応しています。

例3-1: 各顧客の総注文金額と顧客情報を結合する (CTEを使用)

WITH CustomerOrderSummary AS (
    SELECT customer_id, SUM(total_amount) AS total_orders_amount
    FROM orders
    GROUP BY customer_id
)
SELECT c.customer_name, IFNULL(cos.total_orders_amount, 0) AS total_spent
FROM customers c
LEFT JOIN CustomerOrderSummary cos ON c.customer_id = cos.customer_id;

解説
WITH句でCustomerOrderSummaryという名前の一時的な結果セットを定義しています。このCustomerOrderSummaryは、その後のSELECT文で通常のテーブルのように参照できます。導出テーブルと似ていますが、CTEは複数のCTEを連鎖させたり、再帰的に使用したりできる点で、より柔軟で可読性の高いクエリを記述できます。特にクエリが非常に複雑になる場合や、同じサブクエリの結果を複数回使用する場合に有効です。

NOT INとLEFT JOIN + IS NULL / NOT EXISTS の比較

NOT INは直感的ですが、サブクエリがNULLを含む場合に予期せぬ結果(全ての行が返されない)になるという落とし穴があります。そのため、NOT EXISTSまたはLEFT JOIN + IS NULLの方が安全で、一般的に推奨されます。

    • NOT EXISTS (前述の例4-1参照)
    • LEFT JOIN + IS NULL (前述の例1-3参照)
  • NOT INの注意点

    -- もしorders.customer_idにNULLが存在する可能性があれば、このクエリは空の結果を返す可能性がある
    SELECT customer_name
    FROM customers
    WHERE customer_id NOT IN (SELECT customer_id FROM orders);
    

    サブクエリの結果にNULLが含まれる場合、NOT INは常にUNKNOWNとなり、結果的にどの行も選択されません。

  • 複雑なサブクエリの可読性向上
    • 導出テーブル (FROM句内のサブクエリ): 一時的な結果セットを生成し、それをメインクエリで利用します。
    • 共通テーブル式 (CTE / WITH句): MariaDB 10.2以降で利用可能。クエリの論理構造を明確にし、複雑なクエリの可読性と保守性を向上させます。
  • NOT EXISTSサブクエリの代替
    • LEFT JOIN + WHERE ... IS NULL: 関連するデータが存在しない行を効率的に特定するアンチジョインの典型的なパターンです。NOT INよりも安全で推奨されます。
  • EXISTSサブクエリの代替
    • INNER JOIN + DISTINCT: EXISTSと似た結果を得られます。どちらが高速かはケースバイケースでEXPLAINで比較推奨。
  • INサブクエリの代替
    • INNER JOIN: 関連するデータが存在する場合にデータを結合し、GROUP BYDISTINCTで重複を処理します。ほとんどの場合、INより効率的です。