0

最適化しようとしている検索クエリがあります。私は mysql にかなり慣れていないので、誰かが複数の結合でこのタイプのクエリを最適化する方法を説明できますか?

SELECT cust.*, br.branchcode, br.branchname, over.branchcode override_branchcode, over.branchname override_branchname
                    FROM ( SELECT id, CONCAT( firstName, ' ', lastName ) fullName, firstname, lastname, phone1, phone2, mobile1, mobile2, unit, brgy, city, `primary`, override_pst
                    FROM sl_customers ) cust
                    LEFT JOIN sl_branches br ON cust.primary = br.id
                    LEFT JOIN sl_branches over ON cust.override_pst = over.id
                    WHERE fullName LIKE '{$searchtext}' OR firstname LIKE '%{$searchtext}%' OR lastname LIKE '%{$searchtext}%'

なんらかの理由で実行が非常に遅く、脂肪の削減を開始できるかどうか確信が持てません。

4

4 に答える 4

3

first_nameとに適切なインデックスがあってもlast_name、それらを CONCAT すると意味がありません。

私が (何百万ものレコードにわたって) 良い結果を得たアプローチは、アプリケーション ロジックと SQL の組み合わせです。氏名が常にスペースで接続されると仮定すると、検索テキストを (アプリ レベルで) スペースで分割できます。検索テキストに含まれるスペースの数によって、実行するクエリの種類が決まります。

まず、両方の列にインデックスを追加します。

ALTER TABLE `sl_customers` ADD INDEX idx_name_search (`first_name`,`last_name`);

次に、スペースで区切られた名前のすべての順列を作成します。動作する php の例を次に示します。

$search_text = 'millhouse van houten';
$conditions = '';

$parts = explode(' ', $search_text);

for($i=count($parts); $i>=0; $i--){
    $params[] = implode(' ', array_slice($parts, 0, $i)).'%'; //first name
    $params[] = implode(' ', array_slice($parts, $i)).'%'; //last anme

    $conditions .= '(`first_name` LIKE ? AND `last_name` LIKE ?) OR ';
}
$conditions = substr($conditions, 0, -4); //trim the last OR

$query = 'SELECT `first_name`, `last_name` FROM `customer` WHERE '.$conditions;

次のようなクエリになります。

SELECT `first_name`, `last_name` FROM `customer` WHERE 
(`first_name` LIKE ? AND `last_name` LIKE ?) OR 
(`first_name` LIKE ? AND `last_name` LIKE ?) OR 
(`first_name` LIKE ? AND `last_name` LIKE ?) OR 
(`first_name` LIKE ? AND `last_name` LIKE ?);

および次のようなパラメーター

[0] => millhouse van houten%
[1] => %
[2] => millhouse van%
[3] => houten%
[4] => millhouse%
[5] => van houten%
[6] => %
[7] => millhouse van houten%

これにより、次のような組み合わせのセットが検索されます。

first_name             | last_name
-------------------------------------------------
millhouse van houten%  | %
millhouse van%         | houten%
millhouse%             | van houten%
%                      | millhouse van houten%

ほとんどの場合、実際には氏名に含まれるスペースは 1 つだけであるため、私の例よりも比較対象が少なくなることに注意してください。

ワイルドカードを使用したい場合もありますが、インデックスを ( first_name, last_name) ANDのままにしてlast_nameおけば、常にインデックスを効果的に使用できます。比較の開始時にワイルドカードをLIKE使用すると、インデックスの使用が停止します。

長い回答で申し訳ありません - アイデアをできるだけ明確にしたかっただけです。

于 2013-08-03T03:00:38.720 に答える
2

名前は、人々が検索できることを期待するものであり、効率的に検索できます。

おかしな連結をスキップして、テーブルに適切な「フルネーム」列を維持します。それにインデックスを付ければ、インデックス スキャンだけで部分一致でも効率的に実行できます。現時点では、最適化できない計算式をクエリ エンジンに与えることで、クエリ エンジンの顔に唾を吐きかけています。

FULL_NAME でパーシャルを一致させることができれば、FIRST または LAST で個別の OR 句を気にする必要さえないはずです。(ちなみに、OR は非効率的です。)

Michael が言うように、クエリの構造を適切に記述します。CUSTOMER は、サブクエリではなく単純な結合です。

select CUST.*, BR.*, OVER.*            -- you can put in the specific columns.
from SL_CUSTOMERS CUST
join SL_BRANCHES BR on cust.primary = br.id
join SL_BRANCHES OVER on cust.override_pst = over.id
where CUST.FULL_NAME like '%{$searchtext}%';

貧弱な MySQL オプティマイザーに、実際にインデックスを作成して効果的に機能するものを提供すると、ほぼ確実に適切なパフォーマンスが得られます。

参照: http://kristiannielsen.livejournal.com/802.html

于 2013-08-03T01:47:17.217 に答える
1

単語EXPLAINをその前に置き、結果を評価します。非常に大きなフィールド インデックスを探しているため、クエリに時間がかかります。いくつかの新しいキーを作成して、これらのインデックスを最適化します。

于 2013-08-03T01:47:05.607 に答える