我有简单但很长的查询,它计算结果的内容大约需要14秒。主表上的计数本身不到一秒钟,但在多次加入后,延迟太高,如下所示
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
解释的输出是
+---+---+---+---+---+---+---+---+---+---+ | 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 | +---+---+---+---+---+---+---+---+---+---+
更新1
上面的查询与动态where语句$sql = $sql . "Where ". $whereFilter
一起使用,但我以简单的形式提交了它。所以不要认为答案只是简化联接:)
更新2 以下是动态过滤的示例
$temp = $this->province_id;
if ($temp != null) {
$whereFilter = $whereFilter . " and provinces.id In ($temp) ";
}
但是在启动案例中,我们的情况是没有where声明
答案 0 :(得分:7)
左连接总是从第一个表返回一行,但如果有多个匹配的行,则可能返回多行。但是因为您正在计算不同的访问行,所以在连接到另一个表时,计算不同的访问次数与计算访问行数相同。因此,影响结果的唯一连接是内部连接,因此您可以完全删除所有"#34;离开连接表而不影响结果。
我的意思完全""是一些左连接表有效内连接;内部联接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
删除所有这些不必要的连接将极大地改善查询的运行时间,这不仅是因为连接的连接较少,而且因为当您认为大小是 product <时,生成的行集大小可能会很大/ em>所有表格中的匹配项(不是 sum 。
为获得最佳性能,请在所有id-and-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列上有索引。
答案 1 :(得分:3)
您似乎没有任何(或多次)故意过滤。如果您想知道calls
中提到的访问次数,我建议:
select count(distinct c.visit_id)
from calls c;
答案 2 :(得分: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
。
答案 3 :(得分:1)
使用COUNT(visit_id
!=“”然后1结束时的情况)作为访问。
希望这会有所帮助
答案 4 :(得分:1)
不仅仅是:
SELECT COUNT(id)
FROM visits
因为当没有匹配的客户端,...,调用和id应该是唯一的时,所有左外连接也会返回visits.id吗?
不同的提示:一个内连接也仅在客户端存在时有效。通常在需要内部连接时,它们必须尽可能高/靠近源表,因此在您的示例中,在“左连接客户端”之后的行中最好。
答案 5 :(得分:0)
我不太了解你的想法,特别是你的INNER JOIN将在INNER JOIN中转换一些LEFT,这看起来很奇怪,但让我们尝试解决方案:
通常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) ";
}
我认为这有助于您的表现,并且可以根据您的需要运作。