2016-08-12 7 views
8

私は、結果の内容を数えるシンプルで長い質問を約14秒かかります。メインテーブルの数自体は秒未満かかりますが、複数の後遅延に参加する説明の出力は多くの結合でクエリを最適化する方法は?

+---+---+---+---+---+---+---+---+---+---+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+---+---+---+---+---+---+---+---+---+---+ 
| 1 | SIMPLE | specialties | index | PRIMARY | specialty_name | 52 | NULL | 53 | Using index | 
| 1 | SIMPLE | clients | ref | PRIMARY,specialty | specialty | 4 | crm_db.specialties.id | 143 | | 
| 1 | SIMPLE | clients_locations | ref | PRIMARY,client_id | client_id | 4 | crm_db.clients.id | 1 | | 
| 1 | SIMPLE | locations | eq_ref | PRIMARY | PRIMARY | 4 | crm_db.clients_locations.location_id | 1 | | 
| 1 | SIMPLE | districts | eq_ref | PRIMARY | PRIMARY | 4 | crm_db.locations.district_id | 1 | Using where | 
| 1 | SIMPLE | visits | ref | unique_visit,client_location_id | unique_visit | 4 | crm_db.clients_locations.id | 4 | Using index | 
| 1 | SIMPLE | calls | ref | call_unique,visit_id | call_unique | 4 | crm_db.visits.id | 1 | Using index | 
+---+---+---+---+---+---+---+---+---+---+

ある従っ

Select Count(Distinct visits.id) As Count_id 
    From visits 
    Left Join clients_locations ON visits.client_location_id = clients_locations.id 
    Left Join clients ON clients_locations.client_id = clients.id 
    Left Join locations ON clients_locations.location_id = locations.id 
    Left Join users ON visits.user_id = users.id 
    Left Join potentialities ON clients_locations.potentiality = potentialities.id 
    Left Join classes ON clients_locations.class = classes.id 
    Left Join professions ON clients.profession_id = professions.id 
    Inner Join specialties ON clients.specialty_id = specialties.id 
    Left Join districts ON locations.district_id = districts.id 
    Left Join provinces ON districts.province_id = provinces.id 
    Left Join locations_types ON locations.location_type_id = locations_types.id 
    Left Join areas ON clients_locations.area_id = areas.id 
    Left Join calls ON calls.visit_id = visits.id 

としては高すぎるアップデート1 上記のクエリ動的なステートメント$sql = $sql . "Where ". $whereFilterで使用されましたが、私は単純な形式で提出しました。だから、ここでは単に参加する:)

を更新2をeleminate は、動的フィルタリングの例

$temp = $this->province_id; 
if ($temp != null) { 
     $whereFilter = $whereFilter . " and provinces.id In ($temp) "; 
    } 

しかし、ノーどこ声明

+0

訪問時に一意のIDを数えているだけなら、なぜ参加する必要がありますか? – ebyrob

+1

あなたはフィルタリングをしていないようですが、どうして 'count(distinct) 'が必要ですか?つまり、「内部結合」または「結合」の中に2つが振りかざされていますが、結合は必要ないと思われます。 –

+0

後でこのクエリをdynamic whereステートメント 'where $ whereFilter' –

答えて

3

あなたたちのケースで起動する場合である答えを考慮していません意図的なフィルタリングをしていないようです。

select count(distinct c.visit_id) 
from calls c; 
+0

フィルタリングは動的基準に基づいて行われます。 –

+0

フィルタリングをしているときに「十分に速く」動作していますか? –

+0

はい、より多くのフィルタが使用されるほど、結果はより速くなります。 –

7

左が最初のテーブルから行を返す常にに参加するが、複数の一致する行がある場合、複数の行を返すことがあります。あなたはcallsに言及訪問の数を知りたい場合は、私が提案します。しかし、別々の訪問数を数えているので、別の訪問数を数えながら別の表に参加することは、訪問数を数えることと同じです。したがって、結果に影響を与える結合は内部結合だけなので、結合されたすべての "完全"な表を削除して、結果に影響を与えることはできません。

「完全に」とは、左に結合されたテーブルの一部が効果的に内側に結合されていることです。 specialtyへの内部結合は、成功するためにはclientsへの結合を必要とするため、内部結合にもなります。この結合は、成功するためにはclients_locationsへの結合を必要とするため、内部結合にもなります。

あなたのクエリが(掲載さ)にまで低減することができます。不要なすべてのものを削除

Select Count(Distinct visits.id) As Count_id 
From visits 
Join clients_locations ON visits.client_location_id = clients_locations.id 
Join clients ON clients_locations.client_id = clients.id 
Join specialties ON clients.specialty_id = specialties.id 

参加するには、しかし、大幅に少なくするために参加するだけでなく、ためがあるだけでなくので、クエリの実行時間を改善しますあなたのサイズは、すべてのテーブル内の一致(ない合計製品であることを考えると結果の行セットのサイズは巨大である可能性があります。最高のパフォーマンスを得るために

、すべてのID-と-FKでカバーするインデックスを作成します列:

create index visits_id_client_location_id on visits(id, client_location_id); 
create index clients_locations_id_client_id on clients_locations(id, client_id); 
create index clients_id_specialty_id on clients(id, specialty_id); 

可能であれば、インデックスのみのスキャンを使用できます。私は、PK列にインデックスがあると仮定します。

+0

呼び出しはどうですか。私はそれを訪問と一緒に残しておく必要がありますか? –

+0

@eslamいいえ、 'calls'に参加しないでください。別の表への左結合は、常にすべての行を訪問から戻します。左側の結合テーブルに複数の一致する行がある場合は*複数の行を返すことがありますが、* distinct *訪問IDのみをカウントしているため、複数の行に参加しても別の訪問IDは得られません。 – Bohemian

3

プロセス全体を最適化するには、適用するフィルタに応じてSQLを事前に動的に構築することができます。同様に:あなたは複数のフィルタを使用している場合

 

    // base select and left join 
    $preSQL = "Select Count(Distinct visits.id) As Count_id From visits "; 
    $preSQL .= "Left Join clients_locations ON visits.client_location_id = clients_locations.id "; 

    // filtering by province_id 
    $temp = $this->province_id; 
    if ($temp != null) { 
      $preSQL .= "Left Join locations ON clients_locations.location_id = locations.id "; 
      $preSQL .= "Left Join districts ON locations.district_id = districts.id "; 
      $preSQL .= "Left Join provinces ON districts.province_id = provinces.id "; 
      $whereFilter = "provinces.id In ($temp) "; 
     } 

    $sql = $preSQL . "Where ". $whereFilter; 
    // ... 

あなたは、アレイ内のすべての左に参加/内部文字列を置くことができますし、その要求を分析した後、あなたが参加するの最小値を使用して$preSQLを構築することができます。

1

訪問(例:visit_id!= "" THEN 1 END)を訪問として使用します。

SELECT COUNT(id) 
FROM visits 

すべての左外側のときtheresの一致するクライアント、...、通話とidのvisits.idを返すも参加するので:これは

1

はそれだけじゃないのに役立ちます

・ホープユニークであるべきですか?

異なるヒント:1つの内部結合は、クライアントが存在する場合にのみ有効です。一般に、内部結合が必要な場合は、可能な限りソーステーブルに近い/近くに配置する必要があります。そのため、例では、「left join clients」の後の行に最適です。

0

私はあまり自分の考えを理解していなかった、特別にあなたの内に内部結合にそれはいくつかのLEFTをtranformなり、JOIN、それは奇妙なようだが、解決しようとすることができます:

通常LEFTなJOIN非常に悪いパフォーマンスを持っているがWHERE句でそれらを使用する場合にのみ必要になると思います。使用する場合は、INNER JOINでそれらを含めることができます。たとえば :

$query = "Select Count(Distinct visits.id) As Count_id From visits "; 

if($temp != null){ 
    $query .= " INNER JOIN clients_locations ON visits.client_location_id = clients_locations.id "; 
    $query .= " INNER JOIN locations ON clients_locations.location_id = locations.id "; 
    $query .= " INNER JOIN locations ON clients_locations.location_id = locations.id "; 
    $query .= " INNER JOIN districts ON locations.district_id = districts.id " 
    $query .= " INNER JOIN provinces ON districts.province_id = provinces.id "; 
    $whereFilter .= " and provinces.id In ($temp) "; 
} 

は、私はそれがあなたのパフォーマンスを助けると思うし、それはあなたが必要として動作します。

関連する問題