我为我的蛋糕应用程序创建了搜索功能。它是从多个选择框中构建的,您可以在其中选择数据,然后循环选定的选项并将它们实现为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
。
干杯。
答案 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)
加入可能很慢,但这不是你的问题。一个快速解决方法:删除那些子查询,为什么要创建子查询而不是整个表?它使一切都变慢了。
第二:确保您用于加入的所有密钥都标记为索引,它可以使一切更快。