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

データのリストを使用してFIND_IN_SETを使用する方法

    まず、正規化された方法でデータを保存することを検討してください。ここに良い読み物があります:データベースの列に区切りリストを保存するのは本当に悪いですか?

    今-次のスキーマとデータを想定しています:

    create table products (
      id int auto_increment,
      upc varchar(50),
      upc_variation text,
      primary key (id),
      index (upc)
    );
    insert into products (upc, upc_variation) values
      ('01234', '01234,12345,23456'),
      ('56789', '45678,34567'),
      ('056789', '045678,034567');
    

    バリエーションのある商品を探したい'12345' および'34567' 。期待される結果は1行目と2行目です。

    正規化されたスキーマ-多対多の関係

    値をコンマ区切りのリストに格納する代わりに、バリエーションのある製品IDをマップする新しいテーブルを作成します。

    create table products_upc_variations (
      product_id int,
      upc_variation varchar(50),
      primary key (product_id, upc_variation),
      index  (upc_variation, product_id)
    );
    insert into products_upc_variations (product_id, upc_variation) values 
      (1, '01234'),
      (1, '12345'),
      (1, '23456'),
      (2, '45678'),
      (2, '34567'),
      (3, '045678'),
      (3, '034567');
    

    選択クエリは次のようになります:

    select distinct p.*
    from products p
    join products_upc_variations v on v.product_id = p.id
    where v.upc_variation in ('12345', '34567');
    

    ご覧のとおり、正規化されたスキーマを使用すると、非常に基本的なクエリで問題を解決できます。そして、インデックスを効果的に使用できます。

    フルテキストインデックスの「活用」

    (upc_variation)にフルテキストインデックスを使用 使用できるもの:

    select p.*
    from products p
    where match (upc_variation) against ('12345 34567');
    

    これはかなり「きれい」に見え、おそらく効率的です。しかし、この例では機能しますが、機能しない場合は正確に言えないため、このソリューションには満足できません。

    JSON_OVERLAPS()の使用

    MySQL 8.0.17以降、 JSON_OVERLAPS() 。値をJSON配列として保存するか、リストを「オンザフライ」でJSONに変換する必要があります。

    select p.*
    from products p
    where json_overlaps(
      '["12345","34567"]',
      concat('["', replace(upc_variation, ',', '","'), '"]')
    );
    

    これにはインデックスを使用できません。ただし、FIND_IN_SET()ではどちらもできません 。

    JSON_TABLE()の使用

    MySQL 8.0.4以降では、 JSON_TABLE()を使用できます。 「オンザフライ」でデータの正規化された表現を生成します。ここでも、データをJSON配列に格納するか、クエリでリストをJSONに変換します。

    select distinct p.*
    from products p
    join json_table(
      concat('["', replace(p.upc_variation, ',', '","'), '"]'),
      '$[*]' columns (upcv text path '$')
    ) v
    where v.upcv in ('12345', '34567');
    

    ここではインデックスを使用できません。そして、これはおそらくこの回答で提示されたすべての中で最も遅い解決策です。

    RLIKE / REGEXP

    正規表現 を使用することもできます。 :

    select p.*
    from products p
    where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'
    

    dbfiddle.ukのすべてのクエリのデモ を参照してください。



    1. group byを使用して2つのデータベースをクエリし、詳細情報を表示します

    2. 1つの列の値を複数の列の値に分割する

    3. DEFERRABLE INITIALLY IMMEDIATEで定義された制約はまだ延期されていますか?

    4. クエリを使用してSQLServerでテーブルを作成する方法