Mysql et REGEXP

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Mysql et REGEXP

par eleonor » 01 oct. 2007, 12:50

bonjour,

Ok voici mes tables :

les tables "user" et "param" sont liées par "id_contact"
et id_departement de la table "param" est pour l'instant rempli avec les id de la table "departement" ->id_departement, séparer avec des vigules (par exemple : 1,10,45). Ce qui est une erreur apparement.

Code : Tout sélectionner

CREATE TABLE `user` ( `id_contact` int(11) unsigned NOT NULL auto_increment, `nom` varchar(50) NOT NULL default '', `prenom` varchar(50) NOT NULL default '', ... PRIMARY KEY (`id_contact`) ); CREATE TABLE `departement` ( `id_departement` int(10) unsigned NOT NULL auto_increment, `id_region` int(10) unsigned NOT NULL, `nom` varchar(60) NOT NULL, PRIMARY KEY (`id_departement`) ); CREATE TABLE `param` ( `id_param` int(11) unsigned NOT NULL auto_increment, `id_contact` int(11) unsigned NOT NULL default '0', `id_departement` int(20) unsigned NOT NULL default '0', ... PRIMARY KEY (`id_param`) );


La solution serait donc de créer une nouvelle table comme celle-ci :

Code : Tout sélectionner

CREATE TABLE `user_departement` ( `id_selectiondepartement` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `id_contact` INT UNSIGNED NOT NULL , `id_departement` INT UNSIGNED NOT NULL );
Qui enregistrerait toutes les selections des departements par les users.
Est ce que j'ai bien tout compris ?

Merci.

par Hubert Roksor » 27 sept. 2007, 18:28

j'ai maintenant 15000 utilisateurs et si il choisissent 30 départements ca fait 450000 enregistrement et j'ai peur que ca ralentisse un peu la recherche
Avant toute chose, j'aimerais te rassurer en disant que 450 000 enregistrements ce n'est pas vraiment une très grosse table pour une base de données MySQL. À titre de comparaison, quand quelqu'un fait une recherche ici sur les forums avec 1 seul mot-clé, c'est une requête sur une table de plus de 8 millions d'enregistrements qui est exécutée, et pourtant ça ne prend qu'une fraction de seconde.

Sans entrer dans les détails, les indices utilisent une structure de données sous forme d'arbre spécialement adaptée aux gros volumes. Si tu as 15 000 utilisateurs avec 30 départements chacun, on peut estimer que récupérer tous les départements d'un utilisateur demandera de lire 3 nœuds et 30 enregistrements. Avec une solution à base de REGEXP ou de LIKE, la même opération demandera de lire 0 nœuds et 450 000 enregistrements.
j'ai 4 champs de ce type donc faut que je croise sur 4 tables qui contiennent entre 30000 et 500000 enregistrements ?
Il faudrait que tu postes la structure de ces tables sous la forme d'une requête CREATE TABLE ainsi que les relations entre les tables pour qu'on puisse y voir plus clair.

par eleonor » 27 sept. 2007, 18:11

snif snif .... tu es vraiment sûr ? aucune autre alternative ?

Et au point de vue de la charge quand je vais faire mes requetes ?
j'ai 4 champs de ce type donc faut que je croise sur 4 tables qui contiennent entre 30000 et 500000 enregistrements ?

Merci.

par Truc » 27 sept. 2007, 16:42

J'ai bien pensé à un solution ou j'ai une table qui reflète la sélection d'une utilisateur sur les départements choisis mais j'ai maintenant 15000 utilisateurs et si il choisissent 30 départements ca fait 450000 enregistrement et j'ai peur que ca ralentisse un peu la recherche .... mais bon.
en effet c'est ce que tu devrais faire
Des références

par Berzemus » 27 sept. 2007, 16:35

héhé 8-) c'est exactement pour ce genre de problèmes que concaténais des valeurs dans un champ..

Ceci dit, un champ de type set ou enum, qui fonctionnent comme des tableaux, devraient convenir. Mais je n'ais pas encore des masses d'expérience dans le sujet pour te conseiller sur ce qui irait le mieux..

par eleonor » 27 sept. 2007, 15:18

Bonjour,

Ok merci pour vos réponses ..... je sens aussi frémir ce début de défaut de conception ... waouh pas bien !!!

Donc je vous explique plus clairement.

Je fais une mise à jour sur un site existant, permettant quand un utilisateur s'enregistre de sélectionner une ou plusieurs régions et département de son choix. On va uniquement parler du champs département.
donc plein de cases à cocher je récupère tout et je sépare tout les id de département (j'ai une table specique département) par une virgule. et ensuite pour mon moteur de recherche je pensais faire une cherche sur un chiffre en particulier ..... mais apparemment.

J'ai bien pensé à un solution ou j'ai une table qui reflète la sélection d'une utilisateur sur les départements choisis mais j'ai maintenant 15000 utilisateurs et si il choisissent 30 départements ca fait 450000 enregistrement et j'ai peur que ca ralentisse un peu la recherche .... mais bon.

Y a t'il une autre solution ?

Merci pour toutes ces reponses.

par Berzemus » 27 sept. 2007, 13:55

ça me rappelle mes débuts, quand j'enregistrais plusieurs données dans un champ, ne comprenant pas encore très bien l'utilité de la rigueur d'une base SQL, avec des explode() et des implode() en-veux-tu-en-voilà..

reste que regexp est fort utile, un peu comme le replace.

Mais qu'importe, si tu veux bien nous dire à quoi servent tes données, on peut peut-être te proposer une meilleure structure..

par Truc » 27 sept. 2007, 12:28

Moi j'ai envie de dire qu'il y a un défaut de conception de la base de données.

Il ne devrait pas se trouver plusieurs informations dans un même champ (hormis un champ de type set ou enum qui sont prévu pour).

par Berzemus » 27 sept. 2007, 10:16

Voilà, c'était bien ça, essaye ceci:

Code : Tout sélectionner

SELECT * FROM `demande` WHERE `id_mode` REGEXP '[^0-9][0-9][^0-9]' LIMIT 0,20
ça devrait marcher..

en gros:
[^0-9] : un caractère qui est tout sauf un chiffre (^ = n'est pas)
[0-9] : un caractère qui est un chiffre (pour en faire plusieurs, suffit d'en mettre deux ou plus)
et en toute logique : [a-z]: un caractère qui est une lettre (minuscule), etc...

MySQL est plus restrictif (chiant ?) en ce qui concerne le regex, les caractères \d, \w etc.. ne marchent pas.

En tout cas, la, chez moi ça passe nickel.

par eleonor » 26 sept. 2007, 16:30

C'est déjà super comme explication. Tu m'ouvres de nouveaux horizons ;-)

Je vais me renseigner de mon coté, si jamais tu as d'autres d'info n'hesites pas.

Merci encore

par Berzemus » 26 sept. 2007, 15:55

C'est une peu ça, ou deux fois le caractère 'chiffres':

Code : Tout sélectionner

(?<=\D)\d\d(?=\D)
Tu peux aussi répéter le caractère, comme ceci:
\d{2} (il le répète 2 fois)
\d{1,5} (1 ou plusieurs fois, jusque 5, valide aussi bien 145 que 59682)
\d{2,} (au moins 2 fois, jusqu'a l'infini)

Je te donnerais bien un lien vers un tuto, mais je n'en connais pas en français.

Pour la requête mysql, j'utilise rarement cette fonctionnalité de mysql (je préfère la faire sous php), je vais regarder si j'en retrouve un bout. Il faudra sans doute modifier le REGEX, car si je me souviens bien, MySQL utilise une autre implémentation que php (d'autres caractères). J'avais lu un peu vite au début, j'ai légèrement oublié ce petit détail.. :oops:

edit: sans tester, quelque chôse du genre ? (j'expliciterais si ça marche ^-^)

Code : Tout sélectionner

[^0-9][0-9][^0-9]

par eleonor » 26 sept. 2007, 14:26

Salut,

Génial, non seulement une réponse mais une explication claire et précise, merci.

Si je veux recherche une valeur à 2 chiffres par ex 10, je peux faire comme ca ?

Code : Tout sélectionner

(?<=\D)10(?=\D)

J'ai un petit soucis sur ma requete :
$requete="SELECT id_demande,... FROM demande WHERE id_mode REGEXP '(?<=\D)1(?=\D)' LIMIT 0,20";

il faut la formater d'une certaine façon ?

par Berzemus » 26 sept. 2007, 13:39

Salut,

Ton pote Regex, il est un peu froid au premier abord, mais il a des mains d'or:
Si tu veux simplement le chiffre 1 (et non 1 chiffre, genre 4 ou 5), ceci doit faire l'affaire:

Code : Tout sélectionner

(?<=\D)1(?=\D)
En gros, ce regex trouve uniquement des chiffres 1 qui ne sont pas entourés d'autres chiffres (dans ton exemple, soit des virgules (,), soit rien)

On aurait pu éviter le lookahead et le lookbehind en simplifiant:" \D1\D ", mais alors les éléments jouxtants seraient retournés aussi, a moins d'utiliser une parenthèse capturante " \D(1)\D ", mais pourquoi se compliquer la vie ?

Dans le cas ou tu ne veux qu'un chiffre simple (en dessous de 10), il suffit de remplacer le 1:

Code : Tout sélectionner

(?<=\D)\d(?=\D)

voilà.

edit: petite explication, tant qu'a faire:
en regex, \d signifie simplement un chiffre, n'importe lequel. (pour les lettres c'est \w).
Et si tu le mets en majuscule (\D), ben c'est tout sauf un chiffre.

Les lookahead et lookbehind servent a regarder ce qu'il y a avant ou après l'élément.
" (?<=\D) " vérifie que l'élement est précédé par tout sauf un chiffre. le contraire serait " (?<!\D) ", qui chercherait tout sauf ce qui n'est pas un chiffre, ce qui revient à " (?<=\d) ", si tu as bien suivi.

Mysql et REGEXP

par eleonor » 26 sept. 2007, 13:18

Bonjour,

Voila mon soucis, Je veux récupérer les résultats contenant uniquement un chiffre se trouvant dans une chaine de caractère

Concrètement.
Par exemple 6 enregistrements contenant :
15,01,78
12,1,25,2,10
1,10,12
1
14,45,1
12,5,4

Je veux récupérer les résultats contenant uniquement chiffre "1", attention le 12,01,.. ne devront pas être retournés.

J'ai bien un pote qui s'appel REGEXP, mais il est trop bizarre ce mec ;-)

Quelqu'un aurait une idée ?

Merci.