2011-10-12 91 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語句花費相同的時間加載。 –