¿Por qué almacenarías una enumeración en DB?

69

He visto una serie de preguntas, como esta , pidiendo consejos sobre cómo almacenar enumeraciones en DB. Pero me pregunto por qué harías eso. Entonces, digamos que tengo una entidad Personcon un gendercampo y una Genderenumeración. Entonces, mi tabla de persona tiene una columna de género.

Además de la razón obvia de forzar la corrección, no veo por qué crearía una tabla adicional genderpara asignar lo que ya tengo en mi aplicación. Y realmente no me gusta tener esa duplicación.

usuario3748908
fuente
1
¿Dónde más almacenaría datos que pueden cambiar regularmente? Si bien es posible que haya pensado en todas las opciones, ¿qué pasa si alguien aparece y desea agregar una nueva opción? ¿Estás listo para modificar esa lista codificada? Alguien puede desear dar su género como algo diferente de hombre o mujer, por ejemplo, intersexualidad.
JB King
44
@JBKing ... solo mira la lista de géneros de Facebook.
3
Si sus clientes son "Tumblrites engañados", entonces puede crear un esquema de base de datos que le permita crear algo que satisfaga sus necesidades, al menos, si tiene la intención de permanecer en el negocio.
Gort the Robot

Respuestas:

74

Tomemos otro ejemplo que esté menos cargado de concepciones y expectativas. Tengo una enumeración aquí, y es el conjunto de prioridades para un error.

¿Qué valor está almacenando en la base de datos?

Por lo tanto, podría estar almacenando 'C', 'H', 'M', y 'L'en la base de datos. O 'HIGH'y así sucesivamente. Esto tiene el problema de los datos de tipo cadena . Existe un conjunto conocido de valores válidos, y si no está almacenando ese conjunto en la base de datos, puede ser difícil trabajar con él.

¿Por qué está almacenando los datos en el código?

Tienes List<String> priorities = {'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'};o algo por el estilo en el código. Significa que tiene varias asignaciones de estos datos al formato adecuado (está insertando todas las mayúsculas en la base de datos, pero las está mostrando como Critical). Su código ahora también es difícil de localizar. Ha vinculado la representación de la base de datos de la idea a una cadena que se almacena en el código.

En cualquier lugar donde necesite acceder a esta lista, debe tener duplicación de código o una clase con un montón de constantes. Ninguno de los cuales son buenas opciones. Tampoco se debe olvidar que hay otras aplicaciones que pueden usar estos datos (que pueden estar escritos en otros idiomas: la aplicación web Java tiene un sistema de informes Crystal Reports utilizado y un trabajo por lotes de Perl que introduce datos). El motor de informes necesitaría conocer la lista válida de datos (¿qué sucede si no hay nada marcado en 'LOW'prioridad y necesita saber que esa es una prioridad válida para el informe?), Y el trabajo por lotes tendría la información sobre cuál es el valor válido los valores son

Hipotéticamente, podría decir "somos una tienda de un solo idioma, todo está escrito en Java" y tenemos un único .jar que contiene esta información, pero ahora significa que sus aplicaciones están estrechamente unidas entre sí y que contiene .jar los datos. Deberá liberar la parte de informes y la parte de actualización por lotes junto con la aplicación web cada vez que haya un cambio, y esperar que esa versión se realice sin problemas para todas las partes.

¿Qué sucede cuando tu jefe quiere otra prioridad?

Tu jefe vino hoy. Hay una nueva prioridad - CEO. Ahora tiene que ir y cambiar todo el código y volver a compilar y volver a implementar.

Con un enfoque de 'enumeración en la tabla', actualiza la lista de enumeraciones para tener una nueva prioridad. Todo el código que obtiene la lista la extrae de la base de datos.

Los datos rara vez están solos

Con las prioridades, las claves de datos en otras tablas que pueden contener información sobre los flujos de trabajo, o quién puede establecer esta prioridad o no.

Volviendo al género como se menciona en la pregunta por un momento: el género tiene un enlace a los pronombres en uso: he/his/himy she/hers/her... y desea evitar codificarlo en el propio código. Y luego aparece tu jefe y debes agregar que tienes el 'OTHER'género (para simplificarlo) y debes relacionar este género con they/their/them... y tu jefe ve lo que Facebook tiene y ... bueno, sí.

Al restringirse a un bit de datos de tipo secuencial en lugar de una tabla de enumeración, ahora necesita replicar esa secuencia en un montón de otras tablas para mantener esta relación entre los datos y sus otros bits.

¿Qué pasa con otros almacenes de datos?

No importa dónde almacene esto, existe el mismo principio.

  • Podría tener un archivo priorities.propque tenga la lista de prioridades. Usted lee esta lista desde un archivo de propiedades.
  • Podría tener una base de datos del almacén de documentos (como CouchDB ) que tenga una entrada para enums(y luego escribir una función de validación en JavaScript ):

    {
       "_id": "c18b0756c3c08d8fceb5bcddd60006f4",
       "_rev": "1-c89f76e36b740e9b899a4bffab44e1c2",
       "priorities": [ "critical", "high", "medium", "low" ],
       "severities": [ "blocker", "bad", "annoying", "cosmetic" ]
    }
    
  • Podría tener un archivo XML con un poco de esquema:

    <xs:element name="priority" type="priorityType"/>
    
    <xs:simpleType name="priorityType">
      <xs:restriction base="xs:string">
        <xs:enumeration value="critical"/>
        <xs:enumeration value="high"/>
        <xs:enumeration value="medium"/>
        <xs:enumeration value="low"/>
      </xs:restriction>
    </xs:simpleType>
    

La idea central es la misma. El almacén de datos en sí es donde la lista de valores válidos debe almacenarse y aplicarse. Al colocarlo aquí, es más fácil razonar sobre el código y los datos. No tiene que preocuparse por verificar a la defensiva lo que tiene cada vez (¿en mayúsculas o minúsculas? ¿Por qué hay un chriticaltipo en esta columna? Etc ...) porque sabe lo que está obteniendo del almacén de datos. exactamente lo que el almacén de datos espera que envíe de lo contrario, y puede consultar el almacén de datos para obtener una lista de valores válidos.

La comida para llevar

El conjunto de valores válidos son datos , no códigos. Usted no tiene que luchar por la SECO código - pero la cuestión de la duplicación es que se está duplicando los datos en el código, en lugar de respetar su lugar como los datos y su almacenamiento en una base de datos.

Facilita la escritura de múltiples aplicaciones en el almacén de datos y evita tener instancias en las que necesitará implementar todo lo que esté estrechamente vinculado a los datos en sí, porque no ha acoplado su código a los datos.

Hace que las aplicaciones de prueba sean más fáciles porque no tiene que volver a probar toda la aplicación cuando CEOse agrega la prioridad, porque no tiene ningún código que se preocupe por el valor real de la prioridad.

Ser capaz de razonar sobre el código y los datos independientemente uno del otro hace que sea más fácil encontrar y corregir errores al realizar tareas de mantenimiento.

Peter Mortensen
fuente
66
Si puede agregar un valor de enumeración a su código sin tener que cambiar ninguna lógica (y para que no sea la visualización localizada del mismo), dudo de la necesidad del valor de enumeración adicional en primer lugar. Y aunque soy lo suficientemente mayor como para valorar la capacidad de consultar fácilmente las copias de seguridad de la base de datos con consultas SQL simples para analizar un problema, con los ORM en estos días puede hacerlo muy bien sin tener que mirar la base de datos subyacente. Sin embargo, no entiendo el punto sobre la localización (pronombres) aquí: esas cosas ciertamente no deberían estar en una base de datos, sino archivos de recursos de algún tipo, diría.
Voo
1
@Voo los pronombres es un ejemplo de otros datos relacionados con este valor enumerable. Sin los datos en una tabla, los valores tipados en cadena tendrían que estar allí sin restricciones FK adecuadas. Si tiene pronombres (como este) en un archivo de recursos, tiene acoplamiento entre la base de datos y el archivo (actualice la base de datos y vuelva a implementar el archivo). Considere las enumeraciones de Redmine que se pueden modificar a través de la interfaz de administrador sobre la marcha sin tener que volver a implementar .
1
... recuerde también que las bases de datos son un almacén de datos políglotas. Si requiere que la validación se realice como parte del ORM en un idioma, ha hecho necesario duplicar esa validación en cualquier otro idioma que use (recientemente trabajé con un front-end de Java que tenía Python introduciendo datos en la base de datos) - Los sistemas Java ORM y Python tienen que estar de acuerdo en las cosas, y ese acuerdo (los tipos válidos) se implementó más fácilmente al hacer que la base de datos lo aplicara con una tabla 'enum'.
2
@Voo, el uso de enum de Redmine es el mismo que el de bugzilla "la tabla más importante contiene todos los errores del sistema. Se compone de varias propiedades de error, incluidos todos los valores de enum, como la gravedad y la prioridad". - No es un campo de texto de forma libre, es un valor que es uno de este conjunto conocido y enumerable. No es una enumeración de tiempo de compilación , pero todavía es enumerativa. Ver también Mantis .
1
Entonces, para confirmar, ¿su punto es que la gente nunca debería usar Enums? No estaba claro
niico
18

¿Cuál de estos crees que es más probable que produzca errores al leer la consulta?

select * 
from Person 
where Gender = 1

O

select * 
from Person join Gender on Person.Gender = Gender.GenderId
where Gender.Label = "Female" 

Las personas crean tablas de enumeración en SQL porque consideran que este último es más legible, lo que lleva a menos errores al escribir y mantener SQL.

Podrías hacer que el género sea una cadena directamente Person, pero luego tendrías que intentar forzar el caso. También puede aumentar el éxito de almacenamiento para la tabla y el tiempo de consulta debido a la diferencia entre cadenas e enteros, dependiendo de cuán impresionante sea su base de datos para optimizar las cosas.

Telastyn
fuente
55
Pero luego nos unimos a las mesas. Si mi entidad tiene dos enumeraciones, uniré tres tablas solo para una consulta simple.
user3748908
11
@ user3748908 - ¿y qué? Las uniones son para lo que los DB son buenos, y las alternativas son peores, al menos a los ojos de las personas que eligieron esta ruta.
Telastyn
8
@ user3748908: Las bases de datos no solo son realmente buenas para hacer combinaciones, sino que también son muy buenas para garantizar la coherencia. La aplicación de la coherencia funciona muy, muy bien cuando puede apuntar una columna en una tabla a la fila de identificación de otra y decir "el valor de esta columna debe ser uno de los identificadores en esa tabla".
Blrfl
2
Todo esto es cierto, pero hay muchos casos en los que debe sacrificar las uniones por razones de rendimiento. No me malinterpreten, me refiero a este tipo de diseño y unión, pero estoy diciendo que el mundo no va a terminar si descubres que a veces no necesitas las uniones debido al rendimiento.
JonH
3
Si tiene que dejar de unirse a las tablas de referencia por razones de rendimiento @JonH, debe comprar un servidor más grande o dejar de intentar introducir predicados en un gran número de subconsultas (supongo que sabe lo que está haciendo). Las tablas de referencias son las cosas que deberían estar en su caché unos segundos después de iniciar la base de datos.
Ben
10

No puedo creer que la gente no haya mencionado esto todavía.

Llaves extranjeras

Al mantener la enumeración en su base de datos y al agregar una clave externa en la tabla que contiene un valor de enumeración, se asegura de que ningún código ingrese valores incorrectos para esa columna. Esto ayuda a la integridad de sus datos y es la razón más obvia por la que debería tener tablas para enumeraciones.

Benjamin Gruenbaum
fuente
La pregunta tiene solo 5 líneas y establece claramente "Además de la razón obvia de hacer cumplir la corrección". Así que nadie lo ha mencionado porque el OP dice que es obvio y está buscando otras justificaciones. PD: Estoy de acuerdo con usted, esa es una razón suficiente.
user1007074
6

Estoy en el campamento que está de acuerdo contigo. Si mantiene una enumeración de género en su código y un tblGender en su base de datos, puede tener problemas en el momento del mantenimiento. Deberá documentar que estas dos entidades deben tener los mismos valores y, por lo tanto, cualquier cambio que realice en uno también debe realizarlo en el otro.

Luego deberá pasar los valores de enumeración a sus procedimientos almacenados de la siguiente manera:

create stored procedure InsertPerson @name varchar, @gender int
    insert into tblPeople (name, gender)
    values (@name, @gender)

Pero piense cómo haría esto si guardara estos valores en una tabla de base de datos:

create stored procedure InsertPerson @name varchar, @genderName varchar
    insert into tblPeople (name, gender)
    select @name, fkGender
    from tblGender
    where genderName = @genderName --I hope these are the same

Claro que las bases de datos relacionales se crean teniendo en cuenta las uniones, pero ¿qué consulta es más fácil de leer?


Aquí hay otra consulta de ejemplo:

create stored procedure SpGetGenderCounts
    select count(*) as count, gender
    from tblPeople
    group by gender

Compare eso con esto:

create stored procedure SpGetGenderCounts
    select count(*) as count, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender
    group by genderName --assuming no two genders have the same name

Aquí hay otra consulta de ejemplo:

create stored procedure GetAllPeople
    select name, gender
    from tblPeople

Tenga en cuenta que en este ejemplo, tendría que convertir la celda de género en sus resultados de int a enum. Sin embargo, estas conversiones son fáciles. Compare eso con esto:

create stored procedure GetAllPeople
    select name, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender

Todas estas consultas son más pequeñas y más fáciles de mantener cuando se tiene la idea de mantener las definiciones de enumeración fuera de la base de datos.

usuario2023861
fuente
1
¿Qué pasaría si no fuera género? Creo que estamos demasiado obsesionados con el género como campo. ¿Qué pasaría si el OP hubiera dicho "Entonces, digamos que tengo un error de entidad con un campo de prioridad", ¿cambiaría su respuesta?
44
@MichaelT La lista de posibles valores de "prioridad" es parte del código, al menos en la misma medida en que forma parte de los datos. ¿Ves íconos gráficos para varias prioridades? ¿No espera que se retiren de la base de datos? Y cosas así podrían ser temáticas y de estilo y seguir representando el mismo rango de valores almacenados en DB. No puede simplemente cambiarlo en la base de datos de todos modos; Tienes un código de presentación para sincronizar.
Eugene Ryabtsev
1

Crearía una tabla de Genders por la razón de que puede usarse en el análisis de datos. Podría buscar todas las personas de sexo masculino o femenino en la base de datos para generar un informe. Cuantas más formas pueda ver sus datos, más fácil será descubrir la información de tendencias. Obviamente, esta es una enumeración muy simple, pero para enumeraciones complejas (como los países del mundo, o estados), facilita la generación de informes especializados.

zackery.fix
fuente
1

Primero, debe decidir si la base de datos solo será utilizada por una aplicación o si existe la posibilidad de que varias aplicaciones la usen. En algunos casos, una base de datos no es más que un formato de archivo para una aplicación (las bases de datos SQLite a menudo se pueden usar a este respecto). En este caso, duplicar un poco la definición de enumeración como una tabla a menudo puede estar bien y puede tener más sentido.

Sin embargo, tan pronto como desee considerar la posibilidad de tener múltiples aplicaciones accediendo a la base de datos, entonces una tabla para la enumeración tiene mucho sentido (las otras respuestas explican por qué con más detalle). La otra cosa a considerar es que usted u otro desarrollador quiera ver los datos sin procesar de la base de datos. Si es así, esto puede considerarse otro uso de la aplicación (solo uno donde el medidor de laboratorio es SQL sin formato).

Si tiene la enumeración definida en el código (para un código más limpio y la comprobación del tiempo de compilación), así como una tabla en la base de datos, recomendaría agregar pruebas unitarias para verificar que las dos estén sincronizadas.

Eric Johnson
fuente
1

Cuando tiene una enumeración de código que se utiliza para impulsar la lógica de negocios en el código, aún debe crear una tabla para representar los datos en la base de datos por las muchas razones detalladas arriba / abajo. Aquí hay algunos consejos para asegurarse de que sus valores de DB permanezcan sincronizados con los valores del código:

  1. No convierta el campo ID en la tabla en una columna Identidad. Incluir ID y descripción como campos.

  2. Haga algo diferente en la tabla que ayude a los desarrolladores a saber que los valores son semiestáticos / vinculados a una enumeración de código. En todas las demás tablas de búsqueda (generalmente donde los usuarios pueden agregar valores), generalmente tengo un LastChangedDateTime y LastChangedBy, pero no tenerlos en las tablas relacionadas con la enumeración me ayuda a recordar que solo los desarrolladores pueden cambiarlos. Documente esto.

  3. Cree un código de verificación que verifique que cada valor en la enumeración esté en la tabla correspondiente y que solo esos valores estén en la tabla correspondiente. Si tiene "pruebas de estado" automatizadas de aplicaciones que se ejecutan después de la compilación, allí. De lo contrario, haga que el código se ejecute automáticamente al iniciar la aplicación siempre que la aplicación se ejecute en el IDE.

  4. Crear producción entrega scripts SQL que hacen lo mismo, pero desde dentro de la base de datos. Si se crean correctamente, también ayudarán con las migraciones del entorno.

Paul Schirf
fuente
0

Depende también de quién acceda a los datos. Si solo tiene una aplicación, puede estar bien. Si agrega en un almacén de datos o un sistema de informes. Tendrán que saber qué significa ese código, cuál es la versión humana del código.

Por lo general, la tabla de tipos no se duplicaría como una enumeración en el código. Puede cargar la tabla de tipos en una lista que se almacena en caché.

Class GenderList

   Public Shared Property UnfilteredList
   Public Shared Property Male = GetItem("M")
   Public Shared Property Female = GetItem("F")

End Class

A menudo, escribe ir y venir. Necesitaría una fecha para cuando se agregó el nuevo tipo. Sepa cuándo se eliminó un tipo específico. Mostrarlo solo cuando sea necesario. ¿Qué pasa si un cliente quiere "transgénero" como género pero otros clientes no? Toda esta información se almacena mejor en la base de datos.

the_lotus
fuente