Est-il inutile de créer une nouvelle table de base de données au lieu d'utiliser un type de données enum?

38

Supposons que je propose 4 types de services (il est peu probable qu'ils changent souvent):

  • Essai
  • Conception
  • Programmation
  • Autre

Supposons que je dispose de 60 à 80 services réels qui entrent 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:

Utiliser VARCHARdirectement pour encoder directement le type de service sous forme de chaîne

Option 1:

Utiliser la base de données enum. Mais, enum est le mal

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 des constructions de programmation ou de base de données élaborées lors du remplissage ou du traitement de la table. À savoir, les jointures lorsqu’il s’agit de traiter directement avec la base de données ou de créer de nouvelles entités orientées objet du côté de la programmation et de s’assurer que je les exploite correctement.

Option 3:

Ne pas utiliser enum, ne pas utiliser deux tables, mais simplement utiliser une colonne entière

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

Cela ressemble à une "fausse énumération" qui nécessite plus de temps système du côté du code, comme par exemple savoir le savoir {2 == 'Programming'}et le gérer de manière appropriée.

Question:

Actuellement, je l'ai implémenté en utilisant l' option 2 , guidée par des concepts

  1. ne pas utiliser enum (option 1)
  2. éviter d'utiliser une base de données comme feuille de calcul (option 0)

Mais je ne peux pas m'empêcher de penser que cela me semble une perte de temps en termes de programmation et de surcharge cognitive: je dois connaître deux tables et gérer deux tables au lieu d'une.

Pour un «moyen moins coûteux», je regarde Option 3. L'informatique est plus légère et nécessite essentiellement les mêmes constructions de code (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 un gaspillage d’argent et que l’une ou l’autre des options est bien fondée, mais existe-t-il une bonne directive quant au moment où il convient d’utiliser l’option 2 et l’option 3?

Quand il n'y a que deux types (binaire)

Pour ajouter un peu plus à cette question ... dans le même lieu, j'ai une option binaire de service "Standard" ou "Exception", qui peut s'appliquer à l'élément de ligne de service. J'ai encodé cela en utilisant l' option 3 .

J'ai choisi de ne pas créer une nouvelle table uniquement pour contenir les valeurs {"Standard", "Exception"}. Donc, ma colonne ne contient que {0, 1} et mon nom de colonne est appelé exception, et mon code est en train de faire une traduction {0, 1} => {STANDARD, EXCEPTION}(que j'ai encodée comme constantes dans un langage de programmation)

Jusqu'ici, je n'aime pas cette façon non plus ..... (ne pas aimer l'option 2 ni l'option 3). Je trouve que l'option 2 est supérieure à 3, mais avec plus de temps système, 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 un contexte, après avoir lu les réponses - je viens de commencer à utiliser (récemment) un ORM, dans mon cas, Doctrine 2. Après avoir défini le schéma de base de données via Annotations, je souhaitais remplir la base de données. Comme tout mon ensemble de 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_types, puis service_line_items, car il existait une liste provenant d'un tableur réel. Ainsi, les éléments tels que 'standard / exception' et 'Testing' sont tous des chaînes de la feuille de calcul et doivent être codés dans les types appropriés avant de les stocker dans la base de données.

J'ai trouvé ceci SO réponse: Qu'est-ce que vous utilisez à la place d'ENUM dans doctrine2? , qui suggère de ne pas utiliser la construction enum de DB, mais d'utiliser un INTchamp et de coder les types en utilisant la construction 'const' du langage de programmation.

Mais comme le souligne la question SO ci-dessus, je peux éviter d’utiliser directement des entiers et d’utiliser des constructions de langage - des constantes - une fois qu’ils sont définis ....

Mais stringquand même … peu importe comment vous le tournez, si je commence par un type, je dois d'abord le convertir en un type approprié, même si vous utilisez un ORM.

Donc, si $str = 'Testing';je dis , j'ai toujours besoin d'un bloc quelque part qui fait 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 (mais avec des quantités constantes codées), mais le problème est que vous ne pouvez pas automatiquement extraire des objets 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 "il faut encore encoder les choses et gérer les entiers". (D'accord, maintenant, après le commentaire d'Ocramius, je n'aurai pas à traiter directement avec les entiers, mais avec les constantes nommées et une certaine conversion en / à partir de constantes, selon les besoins).

Dennis
la source
9
Quoi que vous fassiez, ne faites pas # 3. Le psychopathe qui le maintient devra constamment comprendre ce que signifient ces nombres magiques. Si vous faites cela, vous feriez mieux d'espérer qu'ils ne sachent pas où vous habitez. blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck le
7
J'aime l'option 2. Si vous n'aimez pas la prolifération des tables de recherche, utilisez une table et ajoutez une colonne "type de recherche". Mais oui, créer une table de consultation est la méthode "standard", car cela vous permet de faire des choses amusantes, comme remplir facilement une liste déroulante dans l'interface utilisateur.
Robert Harvey
N'utilisez pas "EDIT" dans vos messages ici; nous ne sommes pas un forum. Chaque publication Stack Exchange contient déjà un historique détaillé que tout le monde peut voir.
Robert Harvey
si je ne peux pas utiliser EDIT, que dois-je utiliser?
Dennis
Il suffit de modifier le message et de lui donner un aspect naturel, comme je l’ai déjà fait. Consultez l' historique des modifications pour passer en revue les modifications.
Robert Harvey

Réponses:

35

L'option n ° 2, utilisant des tables de référence, est la méthode standard pour le faire. Il a été utilisé par des millions de programmeurs et il fonctionne bien. Il s’agit d’une tendance , de sorte que toute autre personne regardant vos documents saura immédiatement ce qui se passe. Il existe des bibliothèques et des outils qui fonctionnent sur les bases de données, vous évitant beaucoup de travail et vous permettant de le gérer correctement. Les avantages de l'utiliser sont innombrables.

Est-ce un gaspillage? Oui, mais légèrement. Toute base de données semi-décente gardera toujours en mémoire cache les petites tables fréquemment jointes, de sorte que le gaspillage est généralement imperceptible.

Toutes les autres options que vous avez décrites sont ad hoc et hacky, y compris MySQL enum, car elles ne font pas partie du standard SQL. ( enumSinon, l'implémentation de MySQL est fausse, pas l'idée en 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 particulièrement hacky. Vous obtenez le pire des mondes: pas d’intégrité référentielle, pas de valeurs nommées, pas de connaissances définitives de la valeur de la valeur dans la base de données, juste des entiers arbitraires jetés partout. Par ce jeton, vous pouvez également cesser d'utiliser des constantes dans votre code et commencer à utiliser des valeurs codées en dur. circumference = radius * 6.28318530718;. Comment ç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. Se pourrait-il que ce soit parce que vous n'utilisez pas les bons outils pour le travail?

Votre phrase sur «coder des objets et traiter des nombres 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-objet. le mappage (ORM) à la volée dispersé dans le code de votre application, ou dans le meilleur des cas, vous essayez peut-être de lancer votre propre mécanisme de mappage objet-relationnel 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 les mécanismes simples pour bien représenter les éléments dans la base de données.

Enfin, si vous voulez vous simplifier la vie lorsque vous travaillez directement avec la base de données, vous pouvez faire au moins deux choses auxquelles je peux penser maintenant:

  1. Créez des vues qui joignent vos tables principales avec les tables de référence auxquelles elles font référence, de sorte que chaque ligne contienne non seulement les identifiants de référence, mais également les noms correspondants.

  2. 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 bien sûr des mots anglais.) 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 s'appellerait "IsException".

Mike Nakis
la source
3
En passant, Postgres a aussi des types enum . Ils sont simples et n’ont rien de spécial, vous permettant d’utiliser une chaîne lisible comme valeur, mais vous pouvez utiliser un entier plus efficace sous le capot.
Kat
Qu'en est-il du cas où les données sont par conséquent répétées, mais non redondantes (par exemple, ne donneront pas lieu à des anomalies de mise à jour / insertion / suppression)? Par exemple, le sexe d'une personne (peu de chances d'introduire de nouveaux types de données, il ne sera jamais nécessaire de changer le nom d'un genre, etc.)
Adam Thompson
Ceci: parce que finalement, vous découvrirez que vous avez besoin d'un "environnement d'acceptation" et que votre enum ne change pas doit être changé.
Pieter B
3

Option 2 avec constantes ou enums à la fin de la programmation.
Bien que cela fasse double emploi avec une violation du principe de source unique de vérité, vous pouvez y faire face en utilisant la technique de défaillance rapide . Lorsque votre système se charge, il vérifie que les enums ou les valeurs const existent dans la base de données. Sinon, le système devrait lancer une erreur et refuser le chargement. Il sera généralement moins coûteux de corriger ce bogue à ce moment-là que plus tard, quand quelque chose de plus grave pourrait s'être passé.

José Margaça Lopes
la source
0

Rien ne vous empêche d'utiliser des chaînes [courtes] comme clés. Vous pouvez donc toujours avoir la lisibilité des noms dans vos tables et ne pas recourir à un codage sans signification pour les nombres de substitution. Vous devriez toujours avoir le tableau séparé pour décrire les types de service, juste au hasard que, par exemple, votre demande devienne internationale!

Vos utilisateurs peuvent voir vos quatre catégories dans leur propre langue, mais vos tables de base de données contiennent toujours des valeurs que vous pouvez lire - et aucune de celles-ci ne nécessite de modification de la structure de la base de données ou 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'; 
Phill W.
la source