27

テーブルの 1 つに、コンマで区切られた複数の ID を格納する列があります。クエリの「IN」句でこの列の値を使用できる方法はありますか。

column( city) には次のような値があります6,7,8,16,21,2

として使用する必要があります

select * from table where e_ID in (Select city from locations where e_Id=?)

私は Crozin の回答に満足していますが、提案、見解、およびオプションを受け入れます。

あなたの意見を自由に共有してください。

4

11 に答える 11

31

@Jeremy Smith の FIND_IN_SET() の例に基づいて構築すると、結合を使用して実行できるため、サブクエリを実行する必要はありません。

SELECT * FROM table t
JOIN locations l ON FIND_IN_SET(t.e_ID, l.city) > 0
WHERE l.e_ID = ?

これは、とのすべての行の組み合わせに対して FIND_IN_SET() 関数を評価して、テーブル スキャンを実行する必要があるため、パフォーマンスが非常に悪いことが知られています。インデックスを利用できず、改善する方法がありません。tablelocations

悪いデータベース設計を最大限に活用しようとしているとおっしゃっていたことは承知していますが、これがどれほどひどいものであるかを理解する必要があります。

説明: 電話帳で最初、中間、または最後のイニシャルが「J」であるすべての人を検索するように依頼したとします。とにかくすべてのページをスキャンする必要があるため、この場合、本の並べ替えが役立つ方法はありません。

@fthiellaLIKEが提供する解決策には、パフォーマンスに関して同様の問題があります。索引付けできません。

Is storage a delimited list in a database column really that bad?に対する私の回答も参照してください。非正規化されたデータを保存するこの方法の他の落とし穴について。

インデックスを格納するための補助テーブルを作成できる場合は、都市リストの各エントリに場所をマップできます。

CREATE TABLE location2city (
 location INT,
 city INT,
 PRIMARY KEY (location, city)
); 

可能性のあるすべての都市 ( に記載されているものだけでなく) のルックアップ テーブルがあると仮定するとtable、マッピングを生成するための非効率性に 1 回耐えることができます。

INSERT INTO location2city (location, city)
  SELECT l.e_ID, c.e_ID FROM cities c JOIN locations l
  ON FIND_IN_SET(c.e_ID, l.city) > 0;

tableより効率的なクエリを実行して、次のエントリを検索できるようになりました。

SELECT * FROM location2city l
JOIN table t ON t.e_ID = l.city
WHERE l.e_ID = ?;

これにより、インデックスを利用できます。locationsの行の INSERT/UPDATE/DELETE によって、対応するマッピング行も に挿入されることに注意する必要がありますlocation2city

于 2012-11-20T00:49:57.607 に答える
25

INクエリで使用するのではなく、FIND_IN_SET( docs )を使用します。

SELECT * FROM table 
WHERE 0 < FIND_IN_SET(e_ID, (
             SELECT city FROM locations WHERE e_ID=?))

最初の形式の正規化に関する通常の警告が適用されます (データベースは単一の列に複数の値を格納するべきではありません) が、それで行き詰っている場合は、上記のステートメントが役立ちます。

于 2012-11-16T14:43:08.617 に答える
25

MySQL の観点からは、コンマで区切られた複数の ID を格納しているのではなく、「Hello World」または「I like cakes!」とまったく同じ意味を持つテキスト値を格納しています。- つまり、意味がありません。

データベースの 2 つのオブジェクトをリンクする別個のテーブルを作成する必要があります。SQL ベースのデータベースでの多対多または1 対多(要件に応じて) の関係の詳細を参照してください。

于 2012-05-07T10:48:50.003 に答える
22

これは IN 句を使用しませんが、必要なことを行う必要があります。

Select *
from table
where
  CONCAT(',', (Select city from locations where e_Id=?), ',')
  LIKE
  CONCAT('%,', e_ID, ',%')

e_IDただし、コンマや陽気な文字が含まれていないことを確認する必要があります。

例えば

CONCAT(',', '6,7,8,16,21,2', ',') returns ',6,7,8,16,21,2,'

e_ID=1  --> ',6,7,8,16,21,2,' LIKE '%,1,%'  ? FALSE
e_ID=6  --> ',6,7,8,16,21,2,' LIKE '%,6,%'  ? TRUE
e_ID=21 --> ',6,7,8,16,21,2,' LIKE '%,21,%' ? TRUE
e_ID=2  --> ',6,7,8,16,21,2,' LIKE '%,2,%'  ? TRUE
e_ID=3  --> ',6,7,8,16,21,2,' LIKE '%,3,%'  ? FALSE
etc.
于 2012-11-18T22:58:23.813 に答える
8

これがあなたが達成したいことかどうかわかりません。MySQL には、グループGROUP_CONCATから値を連結する機能があります。

次のようなことを試すことができます:

select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?)
于 2012-11-23T07:01:18.117 に答える
7

これはOracle用です..ここで文字列の連結はwm_concatによって行われます

select * from table where e_ID in (Select wm_concat(city) from locations where e_Id=?)

はい、私は raheel shan に同意します .. この "in" 句を入れるには、その列をコードの下の行にする必要があります。

select * from table  where to_char(e_ID) 
in (
  select substr(city,instr(city,',',1,rownum)+1,instr(city,',',1,rownum+1)-instr(city,',',1,rownum)-1) from 
  (
  select ','||WM_CONCAT(city)||',' city,length(WM_CONCAT(city))-length(replace(WM_CONCAT(city),','))+1 CNT from locations where e_Id=? ) TST 
  ,ALL_OBJECTS OBJ where TST.CNT>=rownum
    ) ;
于 2012-11-21T12:22:54.833 に答える
6

あなたが使用する必要があります

FIND_IN_SET値の位置をコンマ区切り値の文字列で返します

mysql> SELECT FIND_IN_SET('b','a,b,c,d');
    -> 2
于 2012-11-22T13:42:06.483 に答える
6

都市の列の値を「分割」する必要があります。次のようになります。

SELECT *
  FROM table
 WHERE e_ID IN (SELECT TO_NUMBER(
                                 SPLIT_STR(city /*string*/
                                           , ',' /*delimiter*/
                                           , 1 /*start_position*/
                                           )
                                 )
                  FROM locations);

MySQL の split_str 関数の詳細については、http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/ を参照してください

また、ここでは Oracle の TO_NUMBER 関数を使用しました。適切な MySQL 関数に置き換えてください。

于 2012-11-23T10:33:57.943 に答える
5

IN 行を取得するため、検索用にコンマ区切りの列を取得しても目的は実行されませんが、このようなデータ ('1','2','3') を提供すると機能しますが、フィールドにこのようなデータを保存することはできません列に何を挿入しても、全体が文字列として取得されます。

于 2012-05-07T10:51:18.283 に答える
2

このように準備済みステートメントを動的に作成できます

set @sql = concat('select * from city where city_id in (',
                  (select cities from location where location_id = 3),
                  ')');
prepare in_stmt from @sql;
execute in_stmt;
deallocate prepare in_stmt;
于 2012-12-30T08:05:19.463 に答える