Administrateur PHPfrance |
3088 Messages
22 janv. 2008, 05:01
Désolé, j'étais un peu pressé lorsque j'ai tapé mon précédent message et je n'ai pas pris le temps de copier/coller les bons liens.
J'ai parlé du type de données "ENUM" dans mon précédent post, c'était une erreur, il fallait vraiment lire 'le type de données "SET"'. Calimero disait que les bitmasks étaient simples et très utilisés, et en effet ils sont tellement utilisés que MySQL possède un type de données (SET) spécialement adapté à leur utilisation. Dans ce message, je vais t'expliquer comment l'utiliser mais avant toute chose j'aimerais précisé que ce n'est pas la solution que je recommanderais. Même si c'est un peu plus compliqué et même un tout petit poil plus lent, je recommanderais la version avec trois tables utilisant une table de relations. Voilà donc pour les avertissements
J'ai créé sur mon système une table "roms_config_language", ainsi qu'une table "tb_jeux" pour pouvoir exécuter des requêtes identiques aux tiennes. Voici cette table :
Code : Tout sélectionner
CREATE TABLE tb_jeux (
id_jeu INT UNSIGNED NOT NULL,
nom VARCHAR(255) NOT NULL,
language SET('Français','Anglais','Chinois','Danois','Néerlandais','Finlandais','Allemand','Italien','Japonais','Norvégien','vide1','Portugais','Espagnol','Suédois','vide2','vide3','Coréen')
) DEFAULT CHARSET=latin1;
INSERT INTO tb_jeux VALUES
(1, 'jeu 1 FR-EN', 'Français,Anglais'),
(2, 'jeu 2', ''),
(3, 'jeu 3 FR-ZH', 'Chinois,Français'),
(4, 'jeu 4 DA-NL-FI', 'Danois,Néerlandais,Finlandais'),
(5, 'jeu 5 FR', 'Français')
Le type de données SET est un type hybride, que l'on peut utiliser à la fois comme une chaine en listant une liste de valeurs séparées par des virgules, ou comme un nombre correspondant au bitmask de ses données. En effet, dans la liste de langues utilisée lors de la définition de la colonne, chaque membre (valeur de la colonne) s'est vue attribuée une valeur correspondant à 1^r (1 puissance
r) où
r est le rang de la valeur à partir de 1. En clair, 'Français' vaut 1, 'Anglais' vaut 2, 'Chinois' vaut 4, etc... Le type SET ne permet pas d'avoir des "trous" dans la progression arithmétique de ses membres, j'ai donc ajoutés des langues "vide" pour les combler et faire correspondre les langues à ton système actuel.
À partir de là, puisque le type SET est considéré comme un nombre et les valeurs sont les même que ton système actuel, je peux réutiliser toutes les requêtes citées dans ce sujet à l'identique
Code : Tout sélectionner
SELECT j.*
FROM tb_jeux j
JOIN roms_config_language l ON l.id_language & j.language > 0
WHERE l.language = 'Français'
Code : Tout sélectionner
+--------+-------------+------------------+
| id_jeu | nom | language |
+--------+-------------+------------------+
| 1 | jeu 1 FR-EN | Français,Anglais |
| 3 | jeu 3 FR-ZH | Français,Chinois |
| 5 | jeu 5 FR | Français |
+--------+-------------+------------------+
Mais le type SET est une chaine aussi, donc je peux l'utiliser directement ! Si je veux récupérer les jeux qui ne sont disponibles qu'en français
Code : Tout sélectionner
+--------+----------+----------+
| id_jeu | nom | language |
+--------+----------+----------+
| 5 | jeu 5 FR | Français |
+--------+----------+----------+
En utilisant la fonction FIND_IN_SET(), les jeux possédant 'Français' dans leur liste
Code : Tout sélectionner
SELECT *
FROM tb_jeux
WHERE FIND_IN_SET('Français', language)
Code : Tout sélectionner
+--------+-------------+------------------+
| id_jeu | nom | language |
+--------+-------------+------------------+
| 1 | jeu 1 FR-EN | Français,Anglais |
| 3 | jeu 3 FR-ZH | Français,Chinois |
| 5 | jeu 5 FR | Français |
+--------+-------------+------------------+
Et pour vérifier que le type s'utilise bien comme un nombre
Code : Tout sélectionner
+--------+-------------+------------------+
| id_jeu | nom | language |
+--------+-------------+------------------+
| 3 | jeu 3 FR-ZH | Français,Chinois |
+--------+-------------+------------------+
Normalement tu devrais maintenant être incollable sur le type SET.

Puisque le type est compatible avec ton système actuel, tu pourrais directement changer le type de ta table sans rien changer à tes requêtes actuelles. Sous phpMyAdmin, tu peux éditer ta colonne, remplacer le type INT par SET et dans le champs valeurs entrer
Code : Tout sélectionner
'Français','Anglais','Chinois','Danois','Néerlandais','Finlandais','Allemand','Italien','Japonais','Norvégien','vide1','Portugais','Espagnol','Suédois','vide2','vide3','Coréen'
La conversion sera automatique (en fait il n'y aura même pas de conversion puisque SET est un type numérique) et si tu ne souhaites plus utiliser SET tu pourras reconvertir ta colonne en INT de la même façon. Mais que cela ne t'empêche pas de faire une copie de tes tables par sécurité.
Attention toutefois, ce système n'est pas aussi flexible qu'un système à 3 tables, ou même ton système de bitmask. Pour ajouter une nouvelle langue il te faudra modifier la colonne à chaque fois. Comme dans ton système de bitmask, il est impossible d'effacer une langue sans la remplacer par une valeur quelconque. De plus, ces valeurs doivent être uniques (on ne peut pas avoir 2 fois 'vide' dans la liste).
Dernière chose, pour ceux qui se poseraient des questions sur les performances, FIND_IN_SET() n'utilise pas les index, comme & et les opérateurs bitwise. D'un autre côté, je remarque qu'il n'y a déjà pas d'index sur roms_config_language.language...