2017-05-16 25 views
0

以下のMySQLクエリでは、PHPを使用して1桁の$セクタと$ subsector_text(カンマ区切りの文字列)を取得します。 $ subsector_textは、1桁または「3,4,7,9」などのいくつかのIDのリストです。コンマで区切られた文字列とコンマで区切られた文字列を照合するSQLクエリ?

$sql = " 
SELECT DISTINCT a.id 
       , a.name 
       , a.category_id 
       , a.sector 
       , a.subsector 
       , a.year_in_operation 
       , a.state 
       , a.total_value 
       , b.country_id 
       , b.project_id 
       , c.isocode_3 
       , c.name 
      FROM com_barchan_project a 
      JOIN com_barchan_location b 
      ON b.project_id = a.id 
      JOIN com_barchan_country c 
      ON c.id = b.country_id 
      JOIN com_barchan_project_value_join d 
      ON a.id = d.project_id 
      WHERE a.state = 1 
      AND a.sector = '$sector' 
      AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$' 
      ORDER 
      BY a.total_value DESC 
       , a.category_id ASC 
       , a.name ASC 
"; 

私は上記のクエリを抱えている問題はラインである:

AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$' 

の$ subsector_text =「3,4,5,9」であれば、それだけでそのレコードを返します$ subsectorフィールドに正確に "3,4,5,9"を含みます。

$ subsector_textのいずれかの値を持つレコードを返すという結果が得られます。たとえば、これらはすべて返されるべきですが、現在はありません。このリストは一例であり、決して正確ではありません。

1,3 
1,5 
1,3,7,9 
3,5 
3,4,5,9 
9 
3 
5 
4 

$ subsector_text文字列の値を持つレコードを選択するようにクエリを変更するにはどうすればよいですか?

注:$ subsector_text = 11の場合、例として次を選択しないでください。

1 
12 
21 

ご協力いただければ幸いです。

+10

データベースでコンマ区切りの値を使用するのが悪いことが分かったと思います。リレーショナルデータベースの慣習に沿ってスキーマを調整することができますか? – tadman

+0

アプリのさまざまな部分を書き直す必要はありません。 –

+1

あなたはこの問題に今や取り組んでいます。たとえそれが巨大な手間であったり、本当に最初にあってはいけないたくさんのコードとたくさんのコードで試してみることができます。これは、克服できない技術的債務がどのように発生するかです。必然性を遅らせる。 – tadman

答えて

0

ソリューションは、アプリケーションをリファクタリングすることでした。数日かかりましたが、問題のコードがなくなり、新しいサブセクターテーブルが作成されました。みんな、ありがとう。

2

カンマ区切り文字列の任意の値と、カンマ区切り文字列の別の値を単一の述語で一致させることは現実的ではありません。

FIND_IN_SET()を使用すると、一度に1つの値を検索できます。

これは、入力を分割することによって得られる値ごとに1つずつ、複数の述語が必要であることを意味します。$subsector_textしたがって、変数を分割し、一連のFIND_IN_SET()呼び出しにマップします。

私は、次のコードをテストしていませんが、それはあなたに私が話しているかのアイデアを与える必要があります。

$subsector_array = array_map('intval', explode(',', $subsector_text)); 
$subsector_terms = array_map(
    function ($id) { return "FIND_IN_SET($id, a.subsector)"; }, 
    $subsector_array); 
$subsector_expr = implode(' OR ', $subsector_terms); 

$sql = " 
SELECT ... 
      WHERE a.state = 1 
      AND a.sector = '$sector' 
      AND ($subsector_expr) 
..."; 

これはもちろん、テーブル・スキャンを強制インデックスに方法はありませんので、 FIND_IN_SET()、または部分文字列を検索するその他の操作。さて、あなたの条件がa.statea.sectorの場合、FIND_IN_SET()条件を適用する前にインデックスを使って検索範囲を絞り込むとします。

私はあなたが継承したシステムで作業しなければならないというジレンマを理解しています。あなたのマネージャーは、これが今設計されているように効率的で信頼できるものではないため、ある時点でこれをリファクタリングする必要があることを知ってもらいましょう。

+0

ありがとうございました。私はそれを撃つだろう。 –

0

あなたのアプローチは正しいですが、いくつかの変更が必要です。代わりに例

... ORに参加し、複数の条件を作成することができ、1つの条件のみ(REGEXP)に一致させようと:

$subsectorArray = explode(',', $subsector_text); 
$or = []; 
foreach ($subsectorArray as $subsector){ 
    $or[] = "a.subsector REGEXP '[^[:alnum:]]{$subsector}[^[:alnum:]]|^{$subsector}[^[:alnum:]]|[^[:alnum:]]{$subsector}$|^{$subsector}$'"; 
} 
$orStr = implode(' OR ', $or); 

$sql = " 
SELECT DISTINCT a.id 
       , a.name 
       , a.category_id 
       , a.sector 
       , a.subsector 
       , a.year_in_operation 
       , a.state 
       , a.total_value 
       , b.country_id 
       , b.project_id 
       , c.isocode_3 
       , c.name 
      FROM com_barchan_project a 
      JOIN com_barchan_location b 
      ON b.project_id = a.id 
      JOIN com_barchan_country c 
      ON c.id = b.country_id 
      JOIN com_barchan_project_value_join d 
      ON a.id = d.project_id 
      WHERE a.state = 1 
      AND a.sector = '$sector' 
      AND ($orStr) 
      ORDER 
      BY a.total_value DESC 
       , a.category_id ASC 
       , a.name ASC 
"; 
+0

するかしない。 「試行」はありません。 ***良い答え***は、何が行われたのか、それがOPのためだけでなく、将来の訪問者のためにそうした方法で行われた理由について、常に説明します。 –

+0

更新しました。アドバイスいただきありがとうございます。 – rafrsr

関連する問題