Aller au contenu

[MySQL] une chaîne comme suite de nombres?


serik

Messages recommandés

Salut,

pour un projet personnel (application Web/Ajax), je cherche à utiliser une chaîne comme suite de nombres.

J'ai 2 tables :

- T1 : elle sert d'index, et qui renvoit une chaîne comme 'N1,N2,N3'

- T2 : plein d'indos, dont les nombres du champ renvoyé par T1 sont les clés primaires.

Je cherche, en une seule requête, à sortir les informations de T2 selon le résultat de T1 :

SELECT * FROM `T2` WHERE `cleprimaire` IN (SELECT `cles` FROM T1 WHERE champ_index=machin).

Après recherches, il n'existe pas de fonction split dans MySQL. Mais est-il possible de lui faire voir la chaîne comme du code? (car les nombres sont déjà séparés par une virgule)

Sous php, si ce n'était pas possible, je ferais :

$cles = requeteSurT1(...);

$resultat = requete(SELECT * FROM `T2` WHERE `cleprimaire` IN ($cles));

Mais ça fait deux requêtes avec une communication PHP<->MySQL inutile.

Je précise qu'il n'est pas possible que je mette l'index dans T1, car plusieurs "champ_index" renvoient sur la même clé, les infos sur chaque row de T2 sont trop importantes pour les dupliquer. Autre info, j'ai plus de 150 000 enregistrements dans T2, et la rapidité de recherche est importante.

Merci par avance,

FL

Lien vers le commentaire
Partager sur d’autres sites

En gros si je comprends bien tu as entre T1 et T2 une relation [n,n]

Il ne faut pas procéder comme cela, tu n'obtiendras jamais de bonne performance en évaluant des clés concaténées dans un champ unique.

Quand tu as une relation [n,n] il faut que tu fasses une table de lien entre tes deux entités.


T1
---------------------------
| ID (int PK) | ... | ... |
---------------------------
| 1           | ... | ... |
---------------------------
| 2           | ... | ... |
---------------------------

T1_APOUR_T2
---------------------------------------
| IDT1 (int PK FK) | IDT2 (int PK FK) |
---------------------------------------
| 1                | 1                |
---------------------------------------
| 1                | 2                |
---------------------------------------
| 1                | 3                |
---------------------------------------
| 2                | 3                |
---------------------------------------
| 2                | 4                |
---------------------------------------


T2
---------------------------
| ID (int PK) | ... | ... |
---------------------------
| 1           | ... | ... |
---------------------------
| 2           | ... | ... |
---------------------------
| 3           | ... | ... |
---------------------------
| 4           | ... | ... |
---------------------------

SELECT *
  FROM
    T1
    LEFT JOIN T1_APOUR_T2 TX ON T1.ID=TX.IDT1
    LEFT JOIN T2 ON TX.IDT2=T2.ID
  WHERE
    T1.ID=1;

Lien vers le commentaire
Partager sur d’autres sites

Je plussoie ce qui est dit au-dessus.

En l'état ce que tu veux faire n'est, à ma connaissance, pas possible.

Voici un cas similaire : forum mysql

Edit : j'en profite pour ajouter que si ta table T2 contient beaucoup d'entrées je te conseille d'éviter les in (val1, val2, .., val550)

Passé un certain nombre de valeurs (j'ai pas de donnés précises, désolé), les in sont non seulement TRES lent, mais bug... enfin du moins c'est ce que j'ai pu constater d'expérience et c'est ce que l'expert sql de ma boîte m'a expliqué.

Ceci étant tu peux parfaitement passer par PHP et faire 2 requêtes... Si t'optimise bien (pense au cache mysql dans le cas de requêtes multiple) c'est complètement transparent.

Lien vers le commentaire
Partager sur d’autres sites

En fait quand je disais "dont les nombres du champ renvoyé par T1 sont les clés primaires" je sous entendais que ce sont les clés primaires de T1, pas de T2.

Pour faire court, T1 devrait être indexé sur le champ T, ce champ contient du texte sous la forme "mot1|mot2|mot3".

Ces mots sont ceux qui sont indexés dans T2, parce que sur certains champs il y a plus de 10 mots (à multiplier par 150 000 entrées).

Une des question que je me pose c'est si une indexation de type FULLTEXT pourrait convenir au champ T.

Sur T2.Champindex, j'ai des requêtes type WHERE Champindex='mot' et de type WHERE Champindex='%mot%'.

Je ne sais pas encore combien de SEQ différents les requêtes vont me renvoyer (une centaine serait probable). Mais y'a t-il possibilité d'aller plus vite? Avec 1 seul SEQ, la requête est de type const. A partir de 2, on passe au type range.

Si je comprends bien ton message Fuinril, il vaut mieux n requêtes qu'une seule regroupant les n? Ou il existe une factorisation possible?

T1
---------------------------
| SEQ (int PK)|  T  | ... |
---------------------------
| 1           |     | ... |
---------------------------
| 2           |     | ... |
---------------------------


T2
-----------------------------
|Champindex (PK)|    SEQ    |
-----------------------------
| a             |     1     |
-----------------------------
| b             |     2     |
-----------------------------
| c             |     3     |
-----------------------------
| d             |     4     |
-----------------------------

Lien vers le commentaire
Partager sur d’autres sites

Ok je reprends avec un exemple "stupide".

Dans T1, j'ai mis : pour séparer les mots (dans ma vraie table c'est un pipe).

Dans T2, les SEQ sont bien séparés par une virgule.

SEQ est un numéro unsigned int, mais les valeurs ne vont pas de 0 à n, ça commence vers 1 000 000.

T1
-------------------------------------------------------------------------------
| SEQ (int PK)|  Mots                                    | Data...            |
-------------------------------------------------------------------------------
| 1           | banane:fraise:kiwi:navet:chou:poireau    | Fruit ou légume... |
-------------------------------------------------------------------------------
| 2           | navet:chou:poireau                       | Légumes...         |
-------------------------------------------------------------------------------


T2
----------------------------------
|Champindex (PK)     |    SEQ    |
----------------------------------
| banane             |     1     |
----------------------------------
| fraise             |     1     |
----------------------------------
| kiwi               |     1     |
----------------------------------
| navet              |    1,2    |
----------------------------------
| choux              |    1,2    |
----------------------------------
| poireau            |    1,2    |
----------------------------------

Le but est d'accéder au champ data de T1 à partir de la liste de la colonne Mots, en passant donc par T2 qui est l'index.

Il n'est pas possible de mettre une entrée par mot dans T1 car les données (Data...) sont trop importantes pour pouvoir dédoubler.

J'ai donc fait une table qui me sert d'index pour T1 (un peu comme on fait un index de recherche pour un forum en ligne).

Voilà, j'espère que c'est plus compréhensible. Désolé de ne pas écrire plus mais j'ai passé la nuit à l'hosto pour me faire recoudre l'annulaire gauche (merci l'hosto de Montréal pour m'avoir fait poireauter de 2h30 AM à 7h15 AM ...). J'ai 3 points de suture, et pas facile de taper avec ça!

Lien vers le commentaire
Partager sur d’autres sites

En fait ton problème c'est que tu refuse de faire une clé primaire composite sur ta table d'index. Ca te fera peut être deux ou trois fois plus d'entrées, mais ce sera beaucoup plus rapide et performant car bien que tu aura plus de lignes tu fera une jointure simple, pas besoin de faire un split ou autre traitement.

Lien vers le commentaire
Partager sur d’autres sites

En fait je ne fais actuellement pas de split, car je récupère les SEQ de T2 dans une première requête, puis dans le script Php je fais un "SELECT * FROM T1 WHERE SEQ IN ($numeros)". (à moins que tu sous-entendes un split de MySQL?)

Je ne refuse pas de faire un index composite sur T2, je n'y avais pas pensé. C'est sur T1 que le dédoublement des données coute cher, pas T2 où il y a rarement plus de deux ou trois numéros ) la fois. Faut juste que je modifie le programme qui génère l'index, c'est pas bien méchant.

Par contre je pensais que la jointure était plus couteuse qu'un SELECT ... IN ; je me trompe?

Par contre, plusieurs SEQ pourraient être en double à la suite de la première requête. Est-il possible de faire un DISTINCT avec le join. A quoi ressemblerait la requête?

Pour l'instant j'ai deux index qui font 6,8 et 9,5Mo (selon que les mots ne contiennent que des lettres ou des idéogrammes).

Lien vers le commentaire
Partager sur d’autres sites

"En fait je ne fais actuellement pas de split, car je récupère les SEQ de T2 dans une première requête, puis dans le script Php je fais un "SELECT * FROM T1 WHERE SEQ IN ($numeros)". (à moins que tu sous-entendes un split de MySQL?)"

Oui, je sous entendais faire un split (ou autre traitement type sous requête) en SQL. Je n'aime pas faire des allers retours serveur applicatif <> BDD, c'est généralement plus couteux qu'une requête unique plus complexe.

Honnêtement, je n'ai jamais fait de test de performance entre une jointure et un IN, mais étant donné que :

- une jointure c'est du SQL super basique

- ton champ de jointure est un int

- qui plus est la clé primaire de ta table principale

- ta condition de jointure est une égalité stricte

Je serai très étonné que cela soit moins performant.

"plusieurs SEQ pourraient être en double à la suite de la première requête"

Si tu fais une recherche sur un mot, c'est impossible d'avoir des SEQ en double car cela briserait la contrainte d'unicité de la clé primaire composite <ChampIndex,SEQ> sur T2

Par contre si tu fais une recherche sur plusieurs mots, alors c'est possible, donc oui, il faudrait faire un DISTINCT (par contre de tête je ne saurais pas te sortir la requête juste, dsl).

Sinon une autre solution, pour éviter de dupliquer tes mots dans T1 (où ils sont concaténés) et T2, serait de retirer entièrement le champ "Mots" de T1.

Tu pourrais récupérer la liste de ces mots en faisant une requête sur T2 (avec ou sans jointure sur T1) :

SELECT GROUP_CONCAT(ChampIndex SEPARATOR ":") AS Mots FROM T2 GROUP BY SEQ WHERE SEQ=1;

>> Mots = "banane:fraise:kiwi:navet:chou:poireau"

Cela évite la duplication de données par contre cela ne t'arrange pas si tu as besoin de récupérer tous les mots d'un SEQ quand tu fais une recherche à partir d'un mot.

Si tu recherche à partir de l'index numérique c'est simple, si tu recherche à partir d'un mot, alors tu devras faire une sous-requête à partir du SEQ pour obtenir la liste complète.

Cela dépends de ton application, si tu n'as pas (ou très rarement) besoin de récupérer la liste des mots à partir d'un mot, alors mieux vaut retirer le duplicata, mais sinon mieux vaut garder le champ car une requête sur un index textuel + jointure + sous-requête avec group_concat, cela commence à faire beaucoup de boulot pour ta BDD.

Lien vers le commentaire
Partager sur d’autres sites

Archivé

Ce sujet est désormais archivé et ne peut plus recevoir de nouvelles réponses.

×
×
  • Créer...