2010-02-01 6 views
3

je une base de données qui contient 5 champs, qui sont: nom, adresse, idcard, ville, rueComment rechercher dans 5 domaines différents avec tous les choix possibles sans ordre particulier dans MySQL utilisant LIKE

je puis une requête PHP qui accepte un paramètre. Dans ce paramètre, vous pouvez entrer les champs que vous voulez et les résultats doivent toujours être précis. Par exemple, si j'entre dans le champ de recherche john doe skate street, vous obtiendrez tous les enregistrements dont le nom est John Doe et ils habitent dans la rue appelée Skate Street.

Notez également que le nom est stocké dans un champ dans l'ordre NOM NOM (je ne peux pas changer cela parce que c'est un must alors s'il vous plaît ne me dites pas qu'il est préférable de le changer ... Je sais que :)). Évidemment, je voudrais que le nom puisse être entré dans n'importe quel ordre, que ce soit NAME NOM ou NOM PRÉNOM.

Mon idée est d'attacher d'abord les deux premiers paramètres entrés comme nom. Je vais dire à mon client que les 2 premiers paramètres doivent être le nom et le prénom ou le nom et le prénom. Après le nom, il peut alors entrer dans une de l'ordre des attributs dans l'ordre qu'il aime, à savoir

Ceux-ci seront acceptés:

name surname idcard town street 
surname name idcard town street 
name surname town idcard street 
surname name address street idcard 

Ils ne seront pas acceptés:

idcard town name surname street 
town name surname idcard 

pour la simple raison que nous compliquerons trop la requête (à cause de la raison du nom/du prénom, et étant dans le même champ)

S'il y a un moyen de rendre cette dernière possible sans trop de complications, j'aimerais l'entendre aussi.

Je vous serais reconnaissant de l'aide à ce sujet

Un grand merci à l'avance

Répondre

4

Le genre de recherche que vous semblez être après est pas vraiment le sweet spot pour SGBDR comme MySQL. Et spécifier les formats de recherche autorisés n'est généralement pas bon pour la facilité d'utilisation, sauf s'il s'agit d'un domaine spécifique.

Garder très générique, la requête pour rechercher trois champs pour « personnes Nom de la rue Faux » pourrait être quelque chose comme ceci:

SELECT * FROM Users 
WHERE (FirstName LIKE "%Persons%" 
OR LastName LIKE "%Persons%" 
OR Address LIKE "%Persons%") 
AND (FirstName LIKE "%Name%" 
OR LastName LIKE "%Name%" 
OR Address LIKE "%Name%") 
AND (FirstName LIKE "%Fake%" 
OR LastName LIKE "%Fake%" 
OR Address LIKE "%Fake%") 
AND (FirstName LIKE "%Street%" 
OR LastName LIKE "%Street%" 
OR Address LIKE "%Street%") 

Cela devrait trouver un membre qui a des détails correspondant à ceux donnés. Mais n'est pas très élégant et ne fera qu'empirer avec des requêtes plus longues et plus de champs. Il est également incroyablement inefficace et se débattra rapidement au fur et à mesure que la table s'allonge - il est incapable d'utiliser les index. Cela ne vous aide pas non plus à obtenir les "meilleures" correspondances en haut (si les résultats sont nombreux)

Une meilleure solution pourrait être d'utiliser l'indexation MySQL en texte intégral en créant une table séparée qui peut être recherchée en utilisant la totalité index de texte pour trouver les utilisateurs pertinents. Je ne connais pas grand chose à cette solution.

Une autre option peut être d'utiliser un outil d'indexation externe tel que Lucene. Alors qu'il ajoute plus de complexité, il permet des fonctionnalités supplémentaires telles que la pondération des champs. Ainsi, le nom pourrait être considéré comme plus important que l'adresse par exemple. Cela peut également ordonner les résultats par ordre de pertinence.

La bonne solution dépend de vos besoins (comme toujours) mais ces idées méritent d'être étudiées.

+0

Je vais essayer de faire fonctionner la méthode que vous avez mentionnée. Pour ce qui est de la convivialité, ce n'est pas un problème, car il s'agit d'un site intranet et, par conséquent, seuls ses employés l'utiliseront. De plus, aucun champ supplémentaire ne sera ajouté. – seedg

+0

Merci pour cela. J'ai des milliers d'enregistrements, cela prenait trop de temps pour qu'une recherche soit complétée. Cependant, j'ai lu sur l'indexation de texte intégral et maintenant les résultats de recherche prennent seulement quelques secondes. Merci beaucoup! – seedg

0

Ceci est moins parfait, mais est donc votre conception afin de ce qu'il vaut:

Tokenize le imput paramater « nom de famille de la rue de la ville » concaténer puis les champs de la base de données et faire une série de produits similaires ou de:

psudocode 
--------- 
where 
name+address+idcard+town+street like %input_token1% or 
name+address+idcard+town+street like %input_token2% or 
... 
name+address+idcard+town+street like %input_token5% 
0

Je voudrais essayer de diviser les mots en utilisant une expression rationnelle et les mettre dans une table temporaire avec Wich vous pouvez joindre à votre table de personne.

Le regex pourrait ressembler à ceci

preg_match_all('/(\S+)*\s+/im', $input, $value, PREG_PATTERN_ORDER); 
for ($i = 0; $i < count($value[0]); $i++) { 
    # Matched text = $result[0][$i]; 
} 

un script qui trouverait vos résultats pourraient ressembler à ceci dans SQL Server. Je n'ai pas assez de connaissances MySQL pour trouver une alternative dans MySQL mais vous devriez avoir l'idée.

L'essentiel de l'idée est de joindre avec la table d'entrée sur chaque colonne que vous souhaitez rechercher. La clause where prend en compte le fait que les valeurs d'entrée sont trouvées dans au moins la quantité de colonnes, car il y a des valeurs d'entrée.

DECLARE @Table TABLE (Name VARCHAR(20), Surname VARCHAR(20), Address VARCHAR(20), IDCard VARCHAR(20), Town VARCHAR(20), Street VARCHAR(20)) 
DECLARE @Inputs TABLE (Value VARCHAR(32)) 

INSERT INTO @Table VALUES ('Doe', 'John', 'Dontknow', 'Dontknow', 'US', 'Skate') 
INSERT INTO @Inputs VALUES ('%John%') 
INSERT INTO @Inputs VALUES ('%Doe%') 
INSERT INTO @Inputs VALUES ('%Skate%') 

SELECT t.* 
FROM @Table t 
     LEFT OUTER JOIN @Inputs i_name ON t.Name LIKE i_name.Value  
     LEFT OUTER JOIN @Inputs i_surname ON t.SurName LIKE i_surname.Value   
     LEFT OUTER JOIN @Inputs i_address ON t.Address LIKE i_address.Value 
     LEFT OUTER JOIN @Inputs i_idcard ON t.IDCard LIKE i_idcard.Value 
     LEFT OUTER JOIN @Inputs i_town ON t.Town LIKE i_town.Value 
     LEFT OUTER JOIN @Inputs i_street ON t.Street LIKE i_street.Value 
     CROSS APPLY (SELECT inputCount = COUNT(*) FROM @Inputs) cnt 
WHERE cnt.inputCount <= 
      CASE WHEN i_name.Value IS NULL THEN 0 ELSE 1 END 
      + CASE WHEN i_surname.Value IS NULL THEN 0 ELSE 1 END 
      + CASE WHEN i_address.Value IS NULL THEN 0 ELSE 1 END 
      + CASE WHEN i_idcard.Value IS NULL THEN 0 ELSE 1 END 
      + CASE WHEN i_town.Value IS NULL THEN 0 ELSE 1 END 
      + CASE WHEN i_street.Value IS NULL THEN 0 ELSE 1 END