Supposons que j'ai 4 types de services que j'offre (il est peu probable qu'ils changent souvent):
Supposons que j'ai 60 à 80 de services réels qui entrent chacun dans l'une des catégories ci-dessus. Par exemple, "un service" peut être "Programme de test utilisant la technique A" et il est de type "Test".
Je veux les encoder dans une base de données. Je suis venu avec quelques options:
Option 0:
Utilisez VARCHAR
directement pour encoder le type de service directement sous forme de chaîne
Option 1:
Utilisez la base de données enum
. Mais, l'énumération est mauvaise
Option 2:
utilisez deux tables:
service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);
Je peux même profiter de l'intégrité référentielle:
ALTER service_line_item
ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);
Ça sonne bien, oui?
Mais je dois encore encoder des choses et gérer des entiers, c'est-à-dire lors du remplissage de la table. Ou je dois créer une programmation élaborée ou des constructions DB lors du remplissage ou du traitement de la table. À savoir, JOINs lorsqu'il s'agit de traiter directement la base de données ou de créer de nouvelles entités orientées objet du côté de la programmation et de m'assurer que je les exploite correctement.
Option 3:
N'utilisez pas enum
, n'utilisez pas deux tables, mais utilisez simplement une colonne entière
service_line_item (
id,
service_type INT, -- use 0, 1, 2, 3 (for service types)
description VARCHAR
);
C'est comme une `` fausse énumération '' qui nécessite plus de surcharge du côté du code, comme par exemple savoir que {2 == 'Programming'}
et y faire face de manière appropriée.
Question:
Actuellement, je l'ai implémenté en utilisant l'option 2 , guidée sous les concepts
Mais je ne peux m'empêcher de penser que cela me semble un gaspillage en termes de programmation et de surcharge cognitive - je dois être au courant de deux tables et gérer deux tables, contre une.
Pour une "manière moins gaspilleuse", je regarde Option 3
. L'informatique est plus légère et nécessite essentiellement les mêmes constructions de code pour fonctionner (avec de légères modifications mais la complexité et la structure sont fondamentalement les mêmes mais avec une seule table)
Je suppose que dans l'idéal, ce n'est pas toujours du gaspillage, et il y a de bons arguments pour l'une ou l'autre option, mais existe-t-il une bonne directive quant au moment où l'on devrait utiliser l'option 2 et quand l'option 3?
Lorsqu'il n'y a que deux types (binaires)
Pour ajouter un peu plus à cette question ... au même endroit, j'ai une option binaire de service "Standard" ou "Exception", qui peut s'appliquer à la ligne de service. J'ai codé cela en utilisant l'option 3 .
J'ai choisi de ne pas créer une nouvelle table juste pour contenir les valeurs {"Standard", "Exception"}. Donc ma colonne contient juste {0, 1} et mon nom de colonne s'appelle exception
, et mon code fait une traduction de {0, 1} => {STANDARD, EXCEPTION}
(que j'ai encodé comme constantes dans le langage de programmation)
Jusqu'à présent, ne pas aimer de cette façon non plus ..... (ne pas aimer l'option 2 ni l'option 3). Je trouve l'option 2 supérieure à 3, mais avec plus de frais généraux, et je ne peux toujours pas échapper au codage sous forme d'entiers, quelle que soit l'option que j'utilise sur 2 et 3.
[~ # ~] orm [~ # ~]
Pour ajouter du contexte, après avoir lu les réponses - je viens de recommencer à utiliser un ORM (récemment), dans mon cas Doctrine 2. Après avoir défini le schéma de base de données via les annotations, je voulais remplir la base de données. Étant donné que l'ensemble de mes données est relativement petit, je voulais essayer d'utiliser des constructions de programmation pour voir comment cela fonctionne.
J'ai d'abord rempli service_type
s, puis service_line_item
s, car il existait une liste à partir d'une feuille de calcul réelle. Donc, des choses comme "standard/exception" et "Test" sont toutes des chaînes sur la feuille de calcul, et elles doivent être encodées dans les types appropriés avant de les stocker dans DB.
J'ai trouvé ceci SO answer: Qu'utilisez-vous à la place d'ENUM dans doctrine2? , qui a suggéré de ne pas utiliser la construction enum de DB, mais d'utiliser un INT
et pour encoder les types en utilisant la construction 'const' du langage de programmation.
Mais comme indiqué dans la question ci-dessus SO, je peux éviter d'utiliser directement des entiers et utiliser des constructions de langage - constantes - une fois qu'elles sont définies ...
Mais quand même ... peu importe comment vous le tournez, si je commence par string
comme type, je dois d'abord le convertir en un type approprié, même lorsque j'utilise un ORM.
Donc, si vous dites $str = 'Testing';
, J'ai encore besoin d'un bloc quelque part qui fasse quelque chose comme:
switch($str):
{
case 'Testing': $type = MyEntity::TESTING; break;
case 'Other': $type = MyEntity::OTHER; break;
}
La bonne chose est que vous ne traitez pas avec des nombres entiers/magiques [au lieu de cela, avec des quantités constantes codées], mais la mauvaise est que vous ne pouvez pas extraire automatiquement des choses dans et hors de la base de données sans cette étape de conversion, à mon connaissance.
Et c'est ce que je voulais dire, en partie, en disant des choses comme "doivent encore coder les choses et gérer les entiers". (Accordé, maintenant, après le commentaire d'Ocramius, je n'aurai pas à traiter directement avec les entiers, mais à gérer les constantes nommées et certaines conversions vers/depuis les constantes, si nécessaire).
L'option n ° 2, utilisant des tableaux de référence, est la manière standard de le faire. Il a été utilisé par des millions de programmeurs et est connu pour fonctionner. C'est un modèle, donc toute personne qui regarde vos affaires saura immédiatement ce qui se passe. Il existe des bibliothèques et des outils qui fonctionnent sur les bases de données, vous épargnant de très nombreux travaux, qui le géreront correctement. Les avantages de son utilisation sont innombrables.
Est-ce du gaspillage? Oui, mais seulement légèrement. Toute base de données à moitié décente gardera toujours ces petites tables fréquemment jointes en cache, donc le gaspillage est généralement imperceptible.
Toutes les autres options que vous avez décrites sont ad hoc et hacky, y compris enum
de MySQL, car elles ne font pas partie du standard SQL. (En dehors de cela, ce qui est nul avec enum
, c'est l'implémentation de MySQL, pas l'idée elle-même. Cela ne me dérangerait pas de la voir un jour faire partie de la norme.)
Votre dernière option n ° 3 avec l'utilisation d'un entier simple est en particulier hacky. Vous obtenez le pire de tous les mondes: aucune intégrité référentielle, aucune valeur nommée, aucune connaissance définitive dans la base de données de ce que représente une valeur, juste des entiers arbitraires jetés partout. De ce fait, vous pouvez tout aussi bien cesser d'utiliser des constantes dans votre code et commencer à utiliser des valeurs codées en dur à la place. circumference = radius * 6.28318530718;
. Et ça?
Je pense que vous devriez réexaminer pourquoi vous trouvez les tableaux de référence onéreux. Personne d'autre ne les trouve onéreux, pour autant que je sache. Serait-ce parce que vous n'utilisez pas les bons outils pour le travail?
Votre phrase sur le fait de devoir "coder des choses et gérer des entiers", ou "créer des constructions de programmation élaborées", ou "créer de nouvelles entités orientées objet du côté de la programmation", me dit que vous essayez peut-être de faire de la relation objet mappage (ORM) à la volée dispersé dans le code de votre application, ou dans le meilleur des cas, vous essayez peut-être de faire rouler votre propre mécanisme de mappage relationnel-objet, au lieu d'utiliser un outil ORM existant pour le travail, tel que Hibernate. Toutes ces choses sont un jeu d'enfant avec Hibernate. Cela prend un peu de temps pour l'apprendre, mais une fois que vous l'avez appris, vous pouvez vraiment vous concentrer sur le développement de votre application et oublier la mécanique de la façon de représenter les choses dans la base de données.
Enfin, si vous voulez vous simplifier la vie en travaillant directement avec la base de données, il y a au moins deux choses que vous pouvez faire, auxquelles je pense en ce moment:
Créez des vues qui joignent vos tables principales avec les tables de référence qu'elles référencent, de sorte que chaque ligne contienne non seulement les identifiants de référence, mais également les noms correspondants.
Au lieu d'utiliser un identifiant entier pour la table de référence, utilisez une colonne CHAR (4), avec des abréviations à 4 lettres. Ainsi, les identifiants de vos catégories deviendraient "TEST", "DSGN", "PROG", "OTHR". (Leurs descriptions resteraient des mots anglais corrects, bien sûr.) Ce sera un peu plus lent, mais croyez-moi, personne ne le remarquera.
Enfin, lorsqu'il n'y a que deux types, la plupart des gens utilisent simplement une colonne booléenne. Ainsi, cette colonne "standard/exception" serait implémentée comme un booléen et elle serait appelée "IsException".
Option 2 avec des constantes ou des énumérations à la fin de la programmation.
Bien qu'il reproduise des connaissances, violant le principe de la source unique de vérité, vous pouvez y faire face en utilisant la technique Fail-fast . Lorsque votre système se charge, il vérifie que les valeurs enums ou const existent dans la base de données. Sinon, le système doit lancer une erreur et refuser de charger. Il sera généralement moins coûteux de corriger ce bogue à ce stade que plus tard, lorsque quelque chose de plus grave aura pu se produire.
l'option # 2 est le choix idéal. Les frais généraux ne sont pas tels qu'ils nécessitent l'examen d'autres options. Avec cette option, la base de données restera organisée et facile à comprendre.
L'option n ° 3 est plus rapide que l'option n ° 2 mais elle vous obligera à garder une trace de quel entier signifie quoi. Si, pour une raison quelconque, si vous souhaitez modifier le numéro, cela peut nécessiter des modifications à de nombreux endroits de votre code. En tant que programmeur, ce qui est sûr, il ne devrait pas y avoir de faille dans l'architecture et il devrait y avoir une place de choix d'où je peux contrôler une tâche spécifique.
Rien ne vous empêche d'utiliser des chaînes [courtes] comme clés, de sorte que vous pouvez toujours avoir la lisibilité des noms dans vos tables et ne pas recourir à un codage de substitution sans signification. Vous devriez toujours avoir le tableau séparé pour décrire les types de services, au cas où, par exemple, votre application deviendrait internationale!
Vos utilisateurs peuvent voir vos quatre catégories en leur propre langue, mais vos tables de base de données contiennent toujours des valeurs que vous peut lire - et aucune d'entre elles ne nécessite de structure de base de données ou de changement de code!
table service_type
( id VARCHAR
, name VARCHAR
primary key ( id )
);
table service_line_item
( id
, service_type VARCHAR
, description VARCHAR
foreign key ( service_type ) references service_type ( id )
);
select * from service_type ;
+-------------+----------------+
| id | name |
+-------------+----------------+
| Testing | Testen |
| Design | Design |
| Programming | Programmierung |
| Other | Andere |
+-------------+----------------+
ou, pour vos clients français ...
update services_types set name = 'Essai' where id = 'Testing';
update services_types set name = 'Conception' where id = 'Design';
update services_types set name = 'Programmation' where id = 'Programming';
update services_types set name = 'Autre' where id = 'Other';