SQL查询的效率

时间:2011-10-12 10:51:24

标签: php mysql


我为我的蛋糕应用程序创建了搜索功能。它是从多个选择框中构建的,您可以在其中选择数据,然后循环选定的选项并将它们实现为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

干杯。

6 个答案:

答案 0 :(得分: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'));

第二,仔细看看每个表的索引。确保您在JOINS中使用的字段有索引。

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

最终:规范化很好,但有时最好结合一些东西,即使这意味着你有重复的数据。

答案 1 :(得分:1)

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

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

$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;

答案 2 :(得分: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号。 (ID号始终需要连接操作才能获得有用的数据;自然键通常会消除连接。)

你已经评论过没有不必要的连接的性能是好的,并且switch语句不应该受到指责。如果这是真的,那么你需要减少连接数。幸运的是,减少连接既简单又直接。

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

加入可能很慢,但这不是你的问题。一个快速解决方法:删除那些子查询,为什么要创建子查询而不是整个表?它使一切都变慢了。

第二:确保您用于加入的所有密钥都标记为索引,它可以使一切更快。