sql >> データベース >  >> RDS >> MariaDB

MariaDBSQL集合演算子

    集合演算子は、さまざまな方法でさまざまな結果セットを組み合わせる処理を行うSQL演算子です。 2つの異なるSELECTがあるとします 単一の結果セットに結合する場合は、集合演算子が機能します。 MariaDBはUNIONをサポートしています およびUNION ALL 長い間集合演算子であり、これらは群を抜いて最も一般的なSQL集合演算子です。

    しかし、ここでは先を進んでいます。まず、使用しているSQL集合演算子とその動作について説明します。これを試してみたい場合は、MariaDBサーバーの既存のデプロイメントを使用するか、MariaDBSkySQLクラウドデータベースで試してみてください。

    UNIONおよびUNIONALL

    UNION およびUNION ALL セット演算子は、2つ以上の結果セットの結果を追加します。 UNION ALLから始めましょう およびUNION その場合、UNION ALLのバリエーションになります 。

    SQLでどのように表示されるかを見てみましょう。私たちがウェブショップを運営していて、私たちが販売する製品については在庫があると仮定しましょう。ここで、注文中または在庫のあるすべての製品を確認したいので、これに対するクエリは次のようになります。

     SELECT oi.prod_id, p.prod_name
       FROM order_items oi JOIN products p ON oi.prod_id = p.id
     UNION ALL
     SELECT i.prod_id, p.prod_name
       FROM inventory i JOIN products p ON i.prod_id = p.id;
    

    これは、集合論では、UNION 注文された製品のセットと在庫にある製品のセットの。これは理論的には問題ありませんが、このクエリの結果には問題があります。問題は、注文と在庫の両方、または在庫の複数の場所に表示される製品が、出力に複数回表示されることです。この問題がUNION ALLの理由です あまり使用されておらず、代わりにUNION DISTINCTDISTINCT はデフォルトであり、無視できます)が使用されます。例:

    SELECT oi.prod_id, p.prod_name
       FROM order_items oi JOIN products p ON oi.prod_id = p.id
     UNION
     SELECT i.prod_id, p.prod_name
       FROM inventory i JOIN products p ON i.prod_id = p.id;

    このクエリでは、注文中または在庫に存在する製品が1回だけリストされます。ここで重複を削除する場合、比較されるのは値であるため、値が異なるテーブルまたは列からのものであっても、同じ列に同じ値を持つ2つの行は等しいと見なされることに注意してください。

    ただし、正直なところ、上記のクエリには、通常のSELECTでは実行できないものはありません。 製品から テーブルといくつかの結合。ある意味でUNION 読みやすいかもしれません。一方、注文中または在庫にある製品のリストが必要で、それがどれであるかを知りたい場合、クエリは次のようになります。

     SELECT 'On order', oi.prod_id, p.prod_name
       FROM order_items oi JOIN products p ON oi.prod_id = p.id
     UNION
     SELECT 'Inventory', i.prod_id, p.prod_name
       FROM inventory i JOIN products p ON i.prod_id = p.id;
    

    単純なSELECTでは簡単に実行できないクエリを次に示します。 製品から productテーブルから同じ行を2回( order_items に対して1回)見ているため、テーブル 在庫に1回 。

    その他のSQL集合演算子

    MariaDB Server 10.3には、Oracleとの互換性を強化するために主に導入された2つの新しいSQL集合演算子が付属していますが、これらの演算子はそれ自体で便利です。次に、MariaDB Server 10.4は、セット演算子の優先順位を制御する機能を追加します。それについても見ていきます。演算子の優先順位を制御する機能がないと、集合演算子は必ずしも期待どおりに機能するとは限りません。

    新しいSQL集合演算子はINTERSECTです。 およびEXCEPT 特に分析を使用する場合に役立ちます。また、JOIN sやその他の構造を代わりに使用できることがよくありますが、SQL集合演算子を使用すると、読みやすく理解しやすいSQL構文を使用できます。また、MariaDBに移行するOracleアプリケーションがある場合、これらの演算子の有用性は明らかです。

    INTERSECT集合演算子

    INTERSECT 演算子は、2つ以上のセットに存在するすべての項目、またはSQL用語では、2つの結果セットに存在するすべての行を返します。この場合、2セットのアイテムの断面が作成されます。 SQL用語では、両方のセットに存在する行のみが返されることを意味します。したがって、注文している製品と在庫にある製品を確認する場合、クエリは次のようになります。

     SELECT oi.prod_id, p.prod_name
       FROM order_items oi JOIN products p ON oi.prod_id = p.id
     INTERSECT
     SELECT i.prod_id, p.prod_name
       FROM inventory i JOIN products p ON i.prod_id = p.id;
    

    この場合も、このクエリはJOINを使用して作成できます。 製品 表ですが、上記のクエリは、私たちが達成しようとしていることについて少し明確です。

    EXCEPT集合演算子

    EXCEPTの場合 演算子、セットの1つにはあるが、他のセットにはないアイテムが必要です。したがって、上記の例を再度使用して、注文しているが在庫がない商品を確認したい場合は、次のようなクエリを作成できます。

     SELECT oi.prod_id, p.prod_name
       FROM order_items oi JOIN products p ON oi.prod_id = p.id
     EXCEPT
     SELECT i.prod_id, p.prod_name
       FROM inventory i JOIN products p ON i.prod_id = p.id;
    

    繰り返しますが、この特定のクエリを作成する方法は他にもありますが、2つの異なるテーブルのデータを組み合わせる場合の、他のより高度なクエリの場合はそうではありません。

    複数のセット演算子の組み合わせ

    これが便利な場合は、3つ以上のセット演算子を組み合わせることができます。たとえば、注文され、配達された、または在庫のある製品を見つけることができるかどうかを見てみましょう。このためのSQLは次のようになります:

    SELECT oi.prod_id, p.prod_name
       FROM order_items oi JOIN products p ON oi.prod_id = p.id
     INTERSECT
     SELECT d.prod_id, p.prod_name
       FROM deliveries d JOIN products p ON d.prod_id = p.id
     UNION
     SELECT i.prod_id, p.prod_name
       FROM inventory i JOIN products p ON i.prod_id = p.id;
    

    これをわかりやすく表現するために、最初に注文された商品と配達された商品を確認し、次にこの商品のセットを在庫内のすべての商品と組み合わせます。結果セットに含まれていない製品は、在庫に含まれていません。 ただし、注文されているか、配達されている可能性がありますが、両方ではありません。

    しかし今、これを別の方法で表現して、何が起こるか見てみましょう。在庫があるか、配達されて注文されているすべての製品のリストが欲しいのですが。その場合、SQLは次のようになります。上記のSQLと似ていますが、わずかに異なります。

     SELECT i.prod_id, p.prod_name
       FROM inventory i JOIN products p ON i.prod_id = p.id
     UNION
     SELECT oi.prod_id, p.prod_name
       FROM order_items oi JOIN products p ON oi.prod_id = p.id
     INTERSECT
     SELECT d.prod_id, p.prod_name
       FROM deliveries d JOIN products p ON d.prod_id = p.id;
    

    では、これをどのように解釈しますか?在庫があり、注文されている製品と配達されている製品をリストしますか?こんな感じですよね?それはまさにそのINTERSECT (およびEXCEPT そのことについては)優先順位 UNION以上 。 2つのSQLステートメントは、少なくともMariaDBで同じ結果セットを生成します。これは、SQL標準が物事が機能する必要があると言っている方法です。ただし、例外があります、Oracle。

    これがOracleでどのように機能するか

    Oracleには、4つのSQL集合演算子(UNION)がすべてあります。 、UNION ALLINTERSECT およびEXCEPT )長い間、標準化されるずっと前から、実装は少し異なります。上記の表を試して、いくつかのデータを挿入してみましょう。データは非常に単純で、あまり成功していない会社を反映していますが、これは例として機能し、ここでは関連する列のみを示しています。

    製品
    テーブル order_items 在庫 配信
    prod_id prod_name order_id prod_id prod_id prod_id
    データ 1 花瓶ブルー 1 1 1 2
    2 花瓶の赤 2 1 2 3
    3 カーペットレッド 2 3

    データが揃ったら、上記の最後のSQLステートメントをもう一度見てみましょう。優先順位を制御できる機能があります。これは、括弧または角かっこを使用することです(MariaDB 10.4で導入、https://jira.mariadb.org/browse/MDEV-11953を参照)。これらを使用して説明します。何が起こっているのか、ステートメントは次のようになります:

     MariaDB> SELECT i.prod_id, p.prod_name
         ->   FROM inventory i JOIN products p ON i.prod_id = p.id
         -> UNION
         -> (SELECT oi.prod_id, p.prod_name
         ->   FROM order_items oi JOIN products p ON oi.prod_id = p.id
         -> INTERSECT
         -> SELECT d.prod_id, p.prod_name
         ->   FROM deliveries d JOIN products p ON d.prod_id = p.id);
     +---------+------------+
     | prod_id | prod_name  |
     +---------+------------+
     |       1 | Vase Blue  |
     |       2 | Vase Red   |
     |       3 | Carpet Red |
     +---------+------------+
     3 rows in set (0.001 sec)
    

    次に、同じ手法を使用して、クエリの3つのコンポーネントを厳密な順序で動作させるようにします。

     MariaDB> (SELECT i.prod_id, p.prod_name
         ->   FROM inventory i JOIN products p ON i.prod_id = p.id
         -> UNION
         -> SELECT oi.prod_id, p.prod_name
         ->   FROM order_items oi JOIN products p ON oi.prod_id = p.id)
         -> INTERSECT
         -> SELECT d.prod_id, p.prod_name
         ->   FROM deliveries d JOIN products p ON d.prod_id = p.id;
     +---------+------------+
     | prod_id | prod_name  |
     +---------+------------+
     |       2 | Vase Red   |
     |       3 | Carpet Red |
     +---------+------------+
     2 rows in set (0.001 sec)
    

    そして最後に括弧なし:

     MariaDB [test]> SELECT i.prod_id, p.prod_name
         ->   FROM inventory i JOIN products p ON i.prod_id = p.id
         -> UNION
         -> SELECT oi.prod_id, p.prod_name
         ->   FROM order_items oi JOIN products p ON oi.prod_id = p.id
         -> INTERSECT
         -> SELECT d.prod_id, p.prod_name
         ->   FROM deliveries d JOIN products p ON d.prod_id = p.id;
     +---------+------------+
     | prod_id | prod_name  |
     +---------+------------+
     |       1 | Vase Blue  |
     |       2 | Vase Red   |
     |       3 | Carpet Red |
     +---------+------------+
     3 rows in set (0.001 sec)
    

    MariaDBは、標準に従って、INTERSECTを想定していることがわかります。 UNIONよりも優先されます 。これでOracleにたどり着きます。 sqlplusを使用してOracleで上記のSQLを試してみましょう:

     SQL> SELECT i.prod_id, p.prod_name
       2   FROM inventory i JOIN products p ON i.prod_id = p.id
       3  UNION
       4  SELECT oi.prod_id, p.prod_name
       5   FROM order_items oi JOIN products p ON oi.prod_id = p.id
       6  INTERSECT
       7  SELECT d.prod_id, p.prod_name
       8   FROM deliveries d JOIN products p ON d.prod_id = p.id;
     
        PROD_ID PROD_NAME
     ---------- ------------------------------
              2 Vase Red
              3 Carpet Red
    

    ここで何が起こっているのですか?ええと、Oracleは標準に準拠していません。異なるセット演算子は同等として扱われ、他の演算子よりも優先されるものはありません。これは、アプリケーションをOracleからMariaDBに移行する場合の問題であり、さらに悪いことに、この違いを見つけるのは難しいということです。エラーは発生せず、データが返され、多くの場合、正しいデータが返されます。ただし、上記の例のようにデータが返される場合は、間違ったデータが返されることがあります。これは問題です。

    データ移行への影響

    では、アプリケーションをOracleからMariaDBに移行する場合、これにどのように対処するのでしょうか。いくつかのオプションがあります:

    • このようなクエリから返されるデータがSQL標準とMariaDBに準拠していると想定するように、アプリケーションを書き直します。
    • MariaDBがOracleと同じデータを返すように、角かっこを使用してSQLステートメントを書き直します
    • または、これが最も賢い方法です。MariaDBSQL_MODE=Oracleを使用してください 設定。

    最後の最も賢い方法として機能するには、MariaDB 10.3.7以降で実行する必要があります(これは、https://jira.mariadb.org/browse/MDEV-13695で実際に提案されました)。これがどのように機能するかを確認しましょう。このSELECTの結果を比較します 上記のOracleのもの(同じ結果を生成します)とその上のMariaDBのもの(そうではありません):

     MariaDB> set SQL_MODE=Oracle;
     Query OK, 0 rows affected (0.001 sec)
     
     MariaDB> SELECT i.prod_id, p.prod_name
         ->   FROM inventory i JOIN products p ON i.prod_id = p.id
         -> UNION
         -> SELECT oi.prod_id, p.prod_name
         ->   FROM order_items oi JOIN products p ON oi.prod_id = p.id
         -> INTERSECT
         -> SELECT d.prod_id, p.prod_name
         ->   FROM deliveries d JOIN products p ON d.prod_id = p.id;
     +---------+------------+
     | prod_id | prod_name  |
     +---------+------------+
     |       2 | Vase Red   |
     |       3 | Carpet Red |
     +---------+------------+
     2 rows in set (0.002 sec)
    

    ご覧のとおり、SQL_MODE Oracleに設定されています 、MariaDBは実際にはOracleのように動作します。 SQL_MODE=Oracleはこれだけではありません もちろんそうですが、あまり知られていない分野の1つです。

    結論

    集合演算子INTERSECT およびEXCEPT あちこちに表示されますが、あまり使用されておらず、いくつかの用途があります。このブログの例は、これらの演算子の実際の優れた使用法を示すことよりも、これらの演算子がどのように機能するかを説明するためのものです。おそらくもっと良い例があります。ただし、OracleからMariaDBに移行する場合、SQL集合演算子は多くのOracleアプリケーションで使用されるため、非常に便利です。ご覧のとおり、MariaDBは、Oracleと同じように機能するように騙される可能性があります。これは、非標準ですが、目的を果たします。ただし、デフォルトでは、もちろんMariaDBは正常に動作し、SQL標準に準拠しています。

    Happy SQL’ing
    / Karlsson


    1. PHPコードを使用してMySQLデータベースに画像をアップロードする方法

    2. ClusterControl 1.7.2の発表:TimescaleDBとMySQL8.0のPostgreSQLバックアップとサポートの改善

    3. MySQLの各グループで最初のレコードを取得する方法

    4. phpMyAdminを介してデータベースをインポートおよびエクスポートする方法(「アクセスが拒否されましたデータベースdb_nameの作成」エラー)