2011-10-12 95 views
0

我已经创建了一个搜索功能给我的蛋糕应用程序。它由多个选择框构建而成,您可以在其中选择数据,然后遍历所选选项并将它们实现为SQL语法。SQL查询效率

基本上是这样的功能看起来的样子:

$selectedFilters = $this->data; 
     $selectSQL = 'SELECT 
         agencies.agency, agencies.website_url, agencies.status, agencies.size, agencies.id, OfficeData.id, ContactData.name, ContactData.surname, ContactData.job_title, ContactData.email, 
         ContactData.mobile, OfficeCountryData.country 
         FROM agencies 
         LEFT JOIN (SELECT agencies_industries.agency_id, agencies_industries.industry_id FROM agencies_industries) AS IndustryData ON agencies.id = IndustryData.agency_id 
         LEFT JOIN (SELECT agencies_professions.agency_id, agencies_professions.profession_id FROM agencies_professions) AS ProfessionData ON agencies.id = ProfessionData.agency_id 
         LEFT JOIN (SELECT agencies_sectors.agency_id, agencies_sectors.sector_id FROM agencies_sectors) AS SectorData ON agencies.id = SectorData.agency_id 
         LEFT JOIN (SELECT agencies_seniorities.agency_id, agencies_seniorities.seniority_id FROM agencies_seniorities) AS SeniorityData ON agencies.id = SeniorityData.agency_id 
         LEFT JOIN (SELECT agencies_zones.agency_id, agencies_zones.zone_id FROM agencies_zones) AS ZonesData ON agencies.id = ZonesData.agency_id 
         LEFT JOIN (SELECT agencies_countries.agency_id, agencies_countries.country_id FROM agencies_countries) AS CountryData ON agencies.id = CountryData.agency_id 
         LEFT JOIN (SELECT agencies_regions.agency_id, agencies_regions.region_id FROM agencies_regions) AS RegionData ON agencies.id = RegionData.agency_id 
         LEFT JOIN (SELECT agencies_cities.agency_id, agencies_cities.city_id FROM agencies_cities) AS CityData ON agencies.id = CityData.agency_id 
         LEFT JOIN (SELECT agencies_specialisms.agency_id, agencies_specialisms.specialism_id FROM agencies_specialisms) AS SpecialismData ON agencies.id = SpecialismData.agency_id 
         LEFT JOIN (SELECT offices.id, offices.agency_id, offices.hq FROM offices WHERE offices.hq = "1") AS OfficeData ON agencies.id = OfficeData.agency_id 
         LEFT JOIN (SELECT countries.id, countries.country FROM countries) AS OfficeCountryData ON OfficeData.hq = OfficeCountryData.id 
         LEFT JOIN (SELECT contacts.name, contacts.surname, contacts.agency_id, contacts.job_title, contacts.email, contacts.mobile FROM contacts) AS ContactData ON agencies.id = ContactData.agency_id 
         '; 
     $whereSQL = ' WHERE 1 = 1 '; 
      foreach($selectedFilters as $key) 
       foreach($key as $name=>$value){ 
        if(is_array($key)) 
         foreach($key as $key=>$value){ 
          $i = 0; 
          $connector = 'AND'; 
          if(is_array($value)){ 
           foreach($value as $value){ 
            if($i > 0) 
             $connector = 'OR'; 
            $i++; 
            switch($key){ 
             case 'Profession': $whereSQL .= $connector.' ProfessionData.profession_id = ' . $value . ' '; 
             break; 
             case 'Specialism': $whereSQL .= $connector.' SpecialismData.specialism_id = ' . $value . ' '; 
             break; 
             case 'SubSpecialism': $whereSQL .= ''; //$whereSQL .= $connector.' SubData.sub_specialism_id = ' . $value . ' '; 
             break; 
             case 'Seniority': $whereSQL .= $connector.' SeniorityData.seniority_id = ' . $value . ' '; 
             break; 
             case 'Industry': $whereSQL .= $connector.' IndustryData.industry_id = ' . $value . ' '; 
             break; 
             case 'Zone': $whereSQL .= $connector.' ZonesData.zone_id = ' . $value . ' '; 
             break; 
             case 'Country': $whereSQL .= $connector.' CountryData.country_id = ' . $value . ' '; 
             break; 
             case 'Region': $whereSQL .= $connector.' RegionData.region_id = ' . $value . ' '; 
             break; 
             case 'City': $whereSQL .= $connector.' CityData.city_id = ' . $value . ' '; 
             break; 
             case 'Sector': $whereSQL .= $connector.' SectorData.sector_id = ' . $value . ' '; 
             break; 
             case 'status': $whereSQL .= $connector.' agencies.status = "' . $value . '" '; 
             break; 
             case 'size': $whereSQL .= $connector.' agencies.size = "' . $value . '" '; 
             break; 
            } 
           } 
          } 
          else 
           if(!isBlank($value) && $key != 'Search') 
            $whereSQL .= $connector.' agencies.'.$key.' = "'.$value.'" '; 
         } 
       } 
     $groupBySQL = 'GROUP BY agencies.id ORDER BY agencies.id ASC'; 
     $resultAgencies = $this->Agency->query($selectSQL . $whereSQL . $groupBySQL); 
     $this->set(compact('resultAgencies')); 

我与我的搜索中遇到的问题是,它的工作原理很慢。发生这种情况是因为使用了太多的LEFT JOIN命令。每个LEFT JOIN从不同的表格中选择数据并将它们全部收集起来,创建另一个表格。然后显示数据。

我需要有人给我一个提示如何做到这一点不使用这么多LEFT JOINs

干杯。

+2

这不是连接是杀死你的查询,但子查询的数量!为什么加入子查询而不是桌子上? – Nin

回答

4

试试这个:

$selectSQL = 'SELECT 
         agencies.agency, agencies.website_url, agencies.status, agencies.size, agencies.id, OfficeData.id, ContactData.name, ContactData.surname, ContactData.job_title, ContactData.email, 
         ContactData.mobile, OfficeCountryData.country 
         FROM agencies 
         LEFT JOIN agencies_industries AS IndustryData ON agencies.id = IndustryData.agency_id 
         LEFT JOIN agencies_professions AS ProfessionData ON agencies.id = ProfessionData.agency_id 
         LEFT JOIN agencies_sectors AS SectorData ON agencies.id = SectorData.agency_id 
         LEFT JOIN agencies_seniorities AS SeniorityData ON agencies.id = SeniorityData.agency_id 
         LEFT JOIN agencies_zones AS ZonesData ON agencies.id = ZonesData.agency_id 
         LEFT JOIN agencies_countries AS CountryData ON agencies.id = CountryData.agency_id 
         LEFT JOIN agencies_regions AS RegionData ON agencies.id = RegionData.agency_id 
         LEFT JOIN agencies_cities AS CityData ON agencies.id = CityData.agency_id 
         LEFT JOIN agencies_specialism AS SpecialismData ON agencies.id = SpecialismData.agency_id 
         LEFT JOIN offices AS OfficeData ON (agencies.id = OfficeData.agency_id AND OfficeData.hq = "1") 
         LEFT JOIN countries AS OfficeCountryData ON OfficeData.hq = OfficeCountryData.id 
         LEFT JOIN contacts AS ContactData ON agencies.id = ContactData.agency_id 
         '; 

但即便如此,因为你加入太多的表可能是缓慢的。但是,如果不了解数据和返回的行数,很难说清楚。如果您只返回几行,您可能需要将某些JOINS移动到子查询(如国家/地区)。或者您可以在单独的查询中添加该信息。

编辑: 不知道你的数据和数据库结构很难说。有很多事情会影响查询的速度。首先重写您的查询,以便您的查询中不使用未用于选择的表(即WHERE)或要显示的字段。所以,如果你毫无选择(emtpy $ selectedFilters)的,你不必包括行业,职业,行业,工龄等表:

$selectedFilters = $this->data; 
     $selectSQL = 'SELECT 
         agencies.agency, agencies.website_url, agencies.status, agencies.size, agencies.id, OfficeData.id, ContactData.name, ContactData.surname, ContactData.job_title, ContactData.email, 
         ContactData.mobile, OfficeCountryData.country 
         FROM agencies'; 


     $sql2='    LEFT JOIN offices AS OfficeData ON (agencies.id = OfficeData.agency_id AND OfficeData.hq = "1") 
         LEFT JOIN countries AS OfficeCountryData ON OfficeData.hq = OfficeCountryData.id 
         LEFT JOIN contacts AS ContactData ON agencies.id = ContactData.agency_id 
         '; 

     $whereSQL = ' WHERE 1 = 1 '; 
      foreach($selectedFilters as $key) 
       foreach($key as $name=>$value){ 
        if(is_array($key)) 
         foreach($key as $key=>$value){ 
          $i = 0; 
          $connector = 'AND'; 
          if(is_array($value)){ 
           foreach($value as $value){ 
            if($i > 0) 
             $connector = 'OR'; 
            $i++; 
            switch($key){ 
             case 'Profession': $whereSQL .= $connector.' ProfessionData.profession_id = ' . $value . ' '; 
             $sql2.=' LEFT JOIN agencies_professions AS ProfessionData ON agencies.id = ProfessionData.agency_id '; 
             break; 
             case 'Specialism': $whereSQL .= $connector.' SpecialismData.specialism_id = ' . $value . ' '; 
             $sql2.=' LEFT JOIN agencies_specialism AS SpecialismData ON agencies.id = SpecialismData.agency_id '; 
             break; 
             case 'SubSpecialism': $whereSQL .= ''; //$whereSQL .= $connector.' SubData.sub_specialism_id = ' . $value . ' '; 
             break; 
             case 'Seniority': $whereSQL .= $connector.' SeniorityData.seniority_id = ' . $value . ' '; 
             $sql2.=' LEFT JOIN agencies_seniorities AS SeniorityData ON agencies.id = SeniorityData.agency_id '; 
             break; 
             case 'Industry': $whereSQL .= $connector.' IndustryData.industry_id = ' . $value . ' '; 
             $sql2=' LEFT JOIN agencies_industries AS IndustryData ON agencies.id = IndustryData.agency_id '; 
             break; 
             case 'Zone': $whereSQL .= $connector.' ZonesData.zone_id = ' . $value . ' '; 
             $sql2.=' LEFT JOIN agencies_zones AS ZonesData ON agencies.id = ZonesData.agency_id '; 
             break; 
             case 'Country': $whereSQL .= $connector.' CountryData.country_id = ' . $value . ' '; 
             $sql2.=' LEFT JOIN agencies_countries AS CountryData ON agencies.id = CountryData.agency_id '; 
             break; 
             case 'Region': $whereSQL .= $connector.' RegionData.region_id = ' . $value . ' '; 
             $sql2.=' LEFT JOIN agencies_regions AS RegionData ON agencies.id = RegionData.agency_id '; 
             break; 
             case 'City': $whereSQL .= $connector.' CityData.city_id = ' . $value . ' '; 
             $sql2.=' LEFT JOIN agencies_cities AS CityData ON agencies.id = CityData.agency_id '; 
             break; 
             case 'Sector': $whereSQL .= $connector.' SectorData.sector_id = ' . $value . ' '; 
             $sql2.='LEFT JOIN agencies_sectors AS SectorData ON agencies.id = SectorData.agency_id '; 
             break; 
             case 'status': $whereSQL .= $connector.' agencies.status = "' . $value . '" '; 
             break; 
             case 'size': $whereSQL .= $connector.' agencies.size = "' . $value . '" '; 
             break; 
            } 
           } 
          } 
          else 
           if(!isBlank($value) && $key != 'Search') 
            $whereSQL .= $connector.' agencies.'.$key.' = "'.$value.'" '; 
         } 
       } 
     $groupBySQL = 'GROUP BY agencies.id ORDER BY agencies.id ASC'; 
     $resultAgencies = $this->Agency->query($selectSQL . $sql2 . $whereSQL . $groupBySQL); 
     $this->set(compact('resultAgencies')); 

其次看看你的索引为每个表很好看。确保你在连接中使用的字段有一个索引。

第三,看看你使用的字段类型。如果SMALLINT足够大,则不要使用INT。

Finaly:标准化非常好,但有时候最好是组合一些东西,即使这意味着您有重复的数据。

+0

''on clause'中有未知列'offices.hq'(是的,在办公室表中有一个列名“hq”。 –

+0

将办公室改为OfficeData(在上面的代码中也改变了) – Nin

+0

还要等就像一分钟的结果(即使没有选择任何数据) –

0

不知道你在做什么,很难判断你的查询是否可以简化。假设你需要所有表的信息,并且所有的id都是主键,我会分析WHERE子句 - 你有索引是否正确定义?使用大型数据库索引会产生巨大差异,并可以大大提高性能

+0

是的,我有索引。 –

0

连接速度很慢,尝试子查询,编写较短的查询。他们可能意味着更多的代码,但如果你在查询前后收集你的时间戳,你会看到巨大的差异。

0

联接可能很慢,但这不是您的问题。一个快速修复:删除这些子查询,为什么做一个子查询而不是整个表?它使一切都变慢很多。

其次:确保您用来加入的所有键都标记为索引,它可以使所有内容都快得多。

+0

如何删除这些子查询? –

+0

只需加入全表,而不是子查询。 – Kris

1

您应该使用连接而不是子查询。你也可能并不总是需要所有那些左连接;我可以看到你的其中语句是动态的,因此作为开关语句的一部分,您可以决定需要加入哪些额外的表。

因此,首先加入你需要列的表;

$selectSQL = " 
    SELECT agencies.agency, 
     agencies.website_url, 
     agencies.status, 
     agencies.size, 
     agencies.id, 
     OfficeData.id, 
     ContactData.name, 
     ContactData.surname, 
     ContactData.job_title, 
     ContactData.email,    
     ContactData.mobile, 
     OfficeCountryData.country 
    FROM agencies 
    LEFT JOIN offices AS OfficeData   ON (agencies.id = OfficeData.agency_id) 
    LEFT JOIN contacts AS ContactData  ON (agencies.id = ContactData.agency_id) 
    LEFT JOIN countries AS OfficeCountryData ON (OfficeData.hq = OfficeCountryData.id) " 

然后当你建立你的where语句时,你可以评估你是否需要在该表中加入该子句才能生效。

$whereSQL = 'WHERE OfficeData.hq = "1"'; 
$joinSQL =''; 

# Loop though your filter options and build up the where and joins 
foreach(...){ 
    switch($key){ 
     case 'Profession': 
      $whereSQL .= $connector.' ProfessionData.profession_id = ' . $value . ' '; 
      $joinSQL .= 'LEFT JOIN agencies_professions AS ProfessionData ON (agencies.id = ProfessionData.agency_id)' 
     break; 
     .... 
    } 
} 

,然后建立你的最终查询

$sql = $selectSQL.' '.$joinSQL.' '.$whereSQL; 
+0

在这种情况下,当选择所有可能的选项时,它将不起作用。 –

+0

我必须更多地了解可能的选项如何工作才能理解这一点。 – Hugh

+0

他们只是多选/选择框。 –

1

学习使用MySQL's EXPLAIN语法。编辑您的问题,并包含您的EXPLAIN计划的输出。

在其他问题中,您的左边加入了许多未选择的表格。尝试这个。

SELECT agencies.agency, agencies.website_url, agencies.status, agencies.size, 
     agencies.id, 
     OfficeData.id, 
     ContactData.name, ContactData.surname, ContactData.job_title, 
     ContactData.email, ContactData.mobile, OfficeCountryData.country 
FROM agencies 
LEFT JOIN (SELECT offices.id, offices.agency_id, offices.hq 
      FROM offices 
      WHERE offices.hq = "1") AS OfficeData 
     ON agencies.id = OfficeData.agency_id 
LEFT JOIN countries AS OfficeCountryData 
     ON OfficeData.hq = OfficeCountryData.id 
LEFT JOIN contacts AS ContactData 
     ON agencies.id = ContactData.agency_id 

这是如何影响性能的?

可能没有令人信服的理由来识别具有身份证号码的国家,城市和地区;他们以自己的名义持有自己的身份。测试用正确的名字替换id号码。 (身份证号码总是需要加入操作才能获得有用的数据;自然键通常会消除连接。)

您已经评论说,没有不必要的连接的性能很好,并且switch声明不是责备。如果这是真的,那么你需要减少连接的数量。幸运的是,减少联接既简单又直接。

如果您必须“报告Universe”,则可以尝试拆分查询并按照同步方式提交多个查询。首先返回并显示,例如代理和联系人数据,您将大大提高应用程序的速度。而dbms可以在第一个渲染时处理第二个查询。经常显然速度比实际速度更重要。

+0

我需要加入所有表格,因为最终结果是动态的。 –

+0

没有连接的性能是应该的。工作非常快。 –

+0

当我注释掉switch语句和循环时,SQL语句花费相同的时间加载。 –