2013-02-01 4 views
1

J'ai la requête MYSQL suivante dans un composant Cold Fusion appelé à partir d'un site Web ColdFusion. J'ai une prise personnalisée qui construit essentiellement une requête booléenne pour les champs suivants: "PlaceName, Pays, Adm1, adm2, adm3, localité".Comment accélérer une requête MYSQL complexe?

<!--- SEARCH PLACES 2---> 
<cffunction name="searchPlaces_full" access="public" returntype="query"> 
    <cfargument name="q" type="string" required="yes"> 


    <CF_BOOLSEARCH searchterm="#arguments.q#" field="PlaceName,Country,Adm1,adm2,adm3,locality " booloperator="and"> 
    <cfquery name="GetPlaces" datasource="#application.settings.dsn#"> 
    SELECT 
     places.CatID, 
     places.PlaceName, 
     places.PlaceID, 
     places.Address1, 
     places.PostalCode, 
     places.Locality, 
     places.Address2, 
     places.ImageThumb, 
     places.Adm1, 
     places.Country, 
     places.Adm2, 
     places.Adm3, 
     places.contributorid, 
     places.lng, 
     places.lat, 
     places.verified, 
     places.verified_by, 
     places.verified_date 
    FROM places INNER JOIN places_cats ON places.PlaceID = places_cats.PlaceID 
    WHERE 
    <cfif len(trim(arguments.q))> 
    (#PreserveSingleQuotes(boolsearch)#) 
    <cfelse> 
    1=0 
    </cfif> 
    AND places_cats.CATID IN (#arguments.categories#) 
    GROUP BY 
     places.CatID, 
     places.PlaceName, 
     places.PlaceID, 
     places.Address1, 
     places.PostalCode, 
     places.Locality, 
     places.Address2, 
     places.ImageThumb, 
     places.Adm1, 
     places.Country, 
     places.Adm2, 
     places.Adm3, 
     places.contributorid, 
     places.lng, 
     places.lat, 
     places.verified, 
     places.verified_by, 
     places.verified_date 
     ORDER BY PlaceName 
     </cfquery> 


    <cfreturn getPlaces> 
</cffunction> 

Il existe 624227 enregistrements dans la base de données. Si je fais une recherche de Chappaqua, le fait SQL qui obtient exécuter est la suivante:

 SELECT 
     places.CatID, 
     places.PlaceName, 
     places.PlaceID, 
     places.Address1, 
     places.PostalCode, 
     places.Locality, 
     places.Address2, 
     places.ImageThumb, 
     places.Adm1, 
     places.Country, 
     places.Adm2, 
     places.Adm3, 
     places.contributorid, 
     places.lng, 
     places.lat, 
     places.verified, 
     places.verified_by, 
     places.verified_date 
    FROM places INNER JOIN places_cats ON places.PlaceID = places_cats.PlaceID 
    WHERE 

    (((PlaceName LIKE '%chappaqua%') OR (Country LIKE '%chappaqua%') OR (Adm1 LIKE '%chappaqua%') OR (adm2 LIKE '%chappaqua%') OR (adm3 LIKE '%chappaqua%') OR (locality LIKE '%chappaqua%'))) 

    AND places_cats.CATID IN (1,21,15,32,16,26,29,27,28,25,75,89,38,5,36,88,87,31,33,24,35,37,90,39,40,34,30,9,8,7,11,20,19,96,97,95,13,17,14,12,3,2,4,84,85,86) 
    GROUP BY 
     places.CatID, 
     places.PlaceName, 
     places.PlaceID, 
     places.Address1, 
     places.PostalCode, 
     places.Locality, 
     places.Address2, 
     places.ImageThumb, 
     places.Adm1, 
     places.Country, 
     places.Adm2, 
     places.Adm3, 
     places.contributorid, 
     places.lng, 
     places.lat, 
     places.verified, 
     places.verified_by, 
     places.verified_date 
     ORDER BY PlaceName 

Je sais qu'il est laid et complexe. Il faut environ 1836ms pour fonctionner. Existe-t-il un meilleur moyen d'écrire la requête ou le code afin qu'il accélère les données renvoyées en moins d'une seconde?

Voici Explain sur le SQL: SQL EXPLAIN

+4

Montrez-nous vos index. Montre-nous ton 'EXPLAIN'. – Kermit

+0

Vous semblez n'utiliser aucune fonction d'agrégation, alors avec le GROUP BY? – Strawberry

+2

@Strawberry Ils essaient d'obtenir des lignes 'DISTINCT' – Taryn

Répondre

4

Le problème est que le moteur est en train de faire une analyse complète de la table de la table, puis trier les résultats pour la group by.

scan Le tableau complet semble presque nécessaire en raison des like s:

(((PlaceName LIKE '%chappaqua%') OR (Country LIKE '%chappaqua%') OR (Adm1 LIKE '%chappaqua%') OR (adm2 LIKE '%chappaqua%') OR (adm3 LIKE '%chappaqua%') OR (locality LIKE '%chappaqua%'))) 

La question est qu'un index sur PlaceName ne peut pas être utilisé, car les caractères initiaux ne sont pas fixes.

Donc. . . Pouvez-vous supprimer le group by? Vous pouvez au moins le remplacer par distinct, bien que je ne pense pas que cela affecterait le plan de requête. Avez-vous un index sur place_cats(placeId, catId)? Cela empêcherait au moins la requête de lire la table de catégories afin qu'elle puisse simplement effectuer des recherches d'index.

Pouvez-vous limiter la recherche aux mots au début des champs?

La seule autre alternative que je peux envisager est de passer à l'utilisation d'un index de texte intégral dans MySQL.

+0

OK ...Raser le joker du début du mot permet d'économiser une demi-seconde, mais je détesterais avoir à faire cela. – SiriusPhil

+0

Oui, j'ai un index sur place_cats (placeId, catId). – SiriusPhil

+0

Oui, cela ne vaudrait-il pas la peine de chercher à faire une recherche en texte intégral à la place de tout ce qui est LIKE? –