web-dev-qa-db-ja.com

複数の条件を持つ1つのクエリで複数の行を選択する

次のようなものを使用して、1つの条件で複数の行を選択できます。

SELECT `Col`, `Col2` FROM `Table` WHERE `Col3` IN (?, ?, ?, ?, ?);
# This selects 5 rows

複数の条件(すべての整数が等しい演算)がある場合、これをどのように実行できますか?

クエリがチェックする必要がある3つの条件があり、これらの3つすべてが複合主キーを構成します。

1つのクエリで10〜100行を選択します(ほとんどの場合10行のみです)。パフォーマンスの点で高速でなければなりません。このため、複数のクエリを使用することはお勧めできません。

CREATE TABLEステートメントは:

CREATE TABLE `Table Name` (
  `X` smallint(6) unsigned NOT NULL,
  `Y` smallint(6) unsigned NOT NULL,
  `Z` smallint(6) unsigned NOT NULL,
  `Data` varchar(2048) NOT NULL DEFAULT '',
  PRIMARY KEY (`X`,`Y`,`Z`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

複数のクエリを使用すると、次のように実行できます。

SELECT `One`, `Two` FROM `Table` WHERE `X` = ? AND `Y` = ? AND `Z` = ?;
SELECT `One`, `Two` FROM `Table` WHERE `X` = ? AND `Y` = ? AND `Z` = ?;
SELECT `One`, `Two` FROM `Table` WHERE `X` = ? AND `Y` = ? AND `Z` = ?;
# This selects 3 rows but I don't want to make 3 calls to the database server for that
7
J. Doe

これを1つのクエリで実行するための2つの基本的な構文オプションと、クエリで値を送信するか、最初にテーブルにロードするかに関する2つのオプションがあります。

  • 通常のAND/OR(括弧はここでは冗長ですが、ORがより複雑になる場合に備えて、WHEREと一緒に使用することをお勧めします):

    WHERE (  ( x = ? AND y = ? AND z = ? )
          OR ( x = ? AND y = ? AND z = ? )
          ... 
          OR ( x = ? AND y = ? AND z = ? )
          )
    
  • 「行コンストラクター」付きのコンパクトIN

    WHERE (x,y,z) IN ((?,?,?), (?,?,?), ...)
    
  • (一時)テーブルとJOINにトリプレットをロードします。

    CREATE (TEMPORARY) TABLE tmp_table 
    --- ;
    
    INSERT INTO tmp_table (x,y,z)
    VALUES (?,?,?), (?,?,?), ... ;
    
    SELECT t.*
    FROM my_table AS t
      JOIN tmp_table AS tmp
      ON  ( t.x = tmp.x AND t.y = tmp.y AND t.z = tmp.z )
     ;
    
  • または:

      ON  (t.x, t.y, t.z) = (tmp.x, tmp.y, tmp.z) 
    

最初の2つのオプションは同等ですが、バージョンによっては効率が異なる場合があります。タプル(行コンストラクター)を含むINのこの構文は、古いバージョンではインデックスを最も効果的に使用しません。 5.7では、オプティマイザーは2つの構文を同等として識別します( MySQLドキュメント:行コンストラクター式の最適化 を参照)。 テスト!

いくつかのパラメーター/トリプレットを照会する場合は、テーブルを使用する他の方法が適している場合があります。 (temp)テーブルにインデックスを付けることもできます。 テスト!

したがって、基本的なアドバイスは、バージョン/構成/セットアップでtestを実行することです。テーブルには、予測されたサイズと同様のサイズがあり、さまざまな数のテーブルがあります。パラメーター。


テストする価値があるかもしれないもう2つの方法:

  • 単純なUNION ALL。パラメータだけが異なる複数の同一のクエリを実行したいだけなので。 1つの欠点は、基本的なクエリが複雑で、チェックするトリプレットが多数ある場合、クエリが非常に長く、見苦しく見えることです。

    SELECT t.* FROM my_table AS t
    WHERE ( x = ? AND y = ? AND z = ? ) UNION ALL
    SELECT t.* FROM my_table AS t
    WHERE ( x = ? AND y = ? AND z = ? ) UNION ALL
    ---
    SELECT t.* FROM my_table AS t
    WHERE ( x = ? AND y = ? AND z = ? ) ;
    
  • 派生テーブルの使用(UNION ALLJOINバリエーション。これは、派生テーブルを具体化してインデックスを作成できる最適化(5.5または5.6で追加されたもの)を使用する場合があります。

    SELECT t.*
    FROM my_table AS t
      JOIN 
          ( SELECT ? AS x, ? AS y, ? AS z  UNION ALL
            SELECT ?, ?, ?  UNION ALL
            ---
            SELECT ?, ?, ?
          )
        AS tmp
      ON  ( t.x = tmp.x AND t.y = tmp.y AND t.z = tmp.z )
     ;
    
11
ypercubeᵀᴹ

MariaDBへのメモ。行コンストラクター式の最適化は機能しません!私のバージョンは10.1.24ですが、10.2のメモにも記載されていません。 PRIMARYではテストしませんでしたが、UNIQUEおよび通常のINDEXではテストしました。

また、AND/ORを使用していて、クエリの長​​さが長くなっている場合(約1200の条件-正確な数値ではありません)、クエリアナライザーは適切なインデックスの選択に失敗するため、クエリにFORCE INDEX句を追加する必要があります。

長いクエリ-悪い分析。行コンストラクターはその日を救ったでしょう。

1
Alex S