2008-10-14 15 views
30

Estoy tratando de diseñar una aplicación para contener información académica de referencia. El problema es que cada tipo diferente de referencia (por ejemplo, artículos de revistas, libros, artículos de periódicos, etc.) requiere información diferente. Por ejemplo, una referencia de revista requiere tanto un título de revista como un título de artículo, y también un número de página, mientras que un libro requiere un editor y una fecha de publicación que los artículos de revista no requieren.¿Una tabla o muchas?

Por lo tanto, debería tener todas las referencias almacenadas en una tabla en mi base de datos y simplemente dejar campos en blanco cuando no se aplican, o debo tener varias tablas como BookReferences, JournalReferences, NewspaperReferences y poner las referencias apropiadas en cada uno. El problema entonces sería que haría la búsqueda a través de todas las referencias algo más difícil, y también la edición tendría que hacerse más bien probablemente por separado.

(tengo la intención de usar Ruby on Rails para este proyecto, por cierto, pero dudo que alguna diferencia a esta pregunta de diseño)

Actualización:

¿Hay más puntos de vista sobre este ? Esperaba obtener una respuesta simple diciendo que un método en particular definitivamente se consideraba "el mejor", pero como siempre, las cosas no son tan simples como esto. La opción de herencia de tabla única parece bastante interesante, pero no hay mucha información sobre ella que pueda encontrar muy fácilmente: puedo publicar otra pregunta en este sitio sobre eso.

Estoy dividido entre Olvak's answer y Corey's answer. La respuesta de Corey da una buena razón por la cual Olvak's no es el mejor, pero la respuesta de Olvak da buenas razones por las cuales Corey's no es el mejor. Nunca me di cuenta de que esto podría ser tan difícil ...

¡Cualquier otro consejo muy apreciado!

+0

Me gusta mucho esta pregunta, gracias. He estado pensando en un problema similar con respecto a una tabla de productos en una configuración de comercio electrónico y las respuestas aquí se pueden aplicar fácilmente a eso. Aclamaciones. – jammus

+0

Me alegro de poder ayudar :-) – robintw

+0

Solo me pregunto: ¿cuántos registros espera tener? Obviamente solo una figura de estadio. Creo que eso también debería ser un factor en la decisión final. – nickf

Respuesta

0

una tabla y un campo "tipo" sería mi sugerencia

34

me gustaría ir por tener una sola tabla para todas las referencias, pero las tablas adicionales como BookReferences y así sucesivamente para los metadatos no se aplica a todos los tipos de referencia.

Buscar y consultar no sería más difícil: después de todo, podría simplemente crear una vista que agregue toda la información como en la solución de tabla única, y luego consultar esa vista aún más.

Tener todo en una tabla con muchos nulos puede parecer la solución más simple, pero en realidad generará muchos problemas. Por ejemplo: con tablas separadas puede definir qué campos son requeridos para cada BookReference, pero si todo está en una tabla, cada campo tiene que ser nulo y, por lo tanto, opcional. También sería más fácil insertar datos no válidos, como una referencia de libro que también contiene erróneamente un nombre de diario no nulo.

Editar: Algunas personas parecen temer unirse. ¡No temas a la unión! Si usa exactamente la misma combinación en varias consultas que de hecho serían tediosas, pero en ese caso la unión debería definirse en una vista , y sus consultas deberían consultar esa vista. Las vistas son realmente la abstracción básica de las bases de datos relacionales, y debe usarlas por las mismas razones por las que utiliza funciones en el código: para evitar la repetición y para encapsular y crear abstracciones.

Editar: Hay algunos comentarios sobre el rendimiento. Es muy difícil adivinar de antemano el rendimiento de los esquemas DB, porque a menudo no es intuitivo.Por ejemplo, una combinación entre varias tablas puede ser más rápida que una exploración de tabla completa de una sola tabla; todo depende del tipo de consulta, la naturaleza de los datos, los índices disponibles, etc. Además, en muchos sistemas de bases de datos puede usar características como vistas materializadas para optimizar el rendimiento para diferentes consultas sin comprometer el modelo lógico. La "desnormalización para el rendimiento" es principalmente culto a la carga en estos días en mi humilde opinión, a menos que seas Google o Flickr.

+2

Usted quitó las palabras de mis manos, ladrón! :) –

+0

El enfoque de administración de documentos (utilizado por Documentum, por ejemplo) –

+0

¿Cómo me vincularía con las otras tablas? Por ejemplo, si el registro 1 en mi tabla de referencia era una referencia de libro y, por lo tanto, estaba vinculado a un registro en la tabla de referencia de libro, ¿cómo iba a saber buscarlo en lugar de hacerlo en la tabla de referencia de diario? – robintw

0

Pregunta sobre la normalización de la base de datos. Jeff Atwood escribió sobre esto en su publicación Maybe Normalizing Isn't Normal. Es una buena lectura.

+1

Una buena lectura tal vez, pero no particularmente bien informado en mi humilde opinión. –

+0

¡Es un artículo horrible! No lo recomiendo en absoluto. Lea los comentarios, o http://codeeleven.blogspot.com/2008/07/normalize-first.html –

4

Tener una sola tabla con el campo "tipo" será problemático cuando se agrega un nuevo tipo de referencia que necesita campos adicionales. La extensión de los valores de campo tipo no es un problema, pero tendría que agregar columnas a la tabla, completar los valores predeterminados para todas las filas actuales, etc.

Tener tablas separadas haría que agregar nuevo tipo de referencia (y generar automáticamente) ¡una forma para ello!) y la búsqueda no sería más difícil.

3

Rails admite la herencia de tabla única y tipos de ActiveRecord polimórficos. Sugeriría investigar esto: ActiveRecord tiene algunas opiniones sobre cómo debería estructurarse la base de datos.

+0

Creo que esta es la idea correcta. El patrón de herencia de tabla única no es específico de Rails. –

3

Creo que debe anticiparse a cómo se verá el SQL para cada una de las soluciones. Si pasas por ese ejercicio, entonces encontrarás que poner todo en una tabla es el más fácil de codificar y probablemente te lleve a tener el mejor rendimiento. Es más fácil separar las cosas que quieres de una tabla, luego es juntar las cosas de varias tablas.

permite decir mi-uno-big-tabla es el siguiente:

1 Identificación del
2 Tipo 3
campo común a-libro-y-revista
4 campo específico-a- libro
5 campo específico a diario

Si estoy interesado sólo en los libros, que puede crear una vista, o simplemente SQL sin formato, como este:

create view book as 
select id, field_common-to-book-and-journal, field-specific-to-book 
from my-one-big-table 
where type = 'book' 

Por lo tanto, es fácil simular que los datos están en tablas separadas cuando quiero.

Pero, si comienzo al poner los datos en tablas separadas a continuación, voy a terminar de escribir SQL como esto:

select id, field-common-to-book-and-journal from books 
union 
select id, field-common-to-book-and-journal from journal-articles 
union 
.... etc, for each type 

No sé acerca de otras bases de datos, pero haciendo los sindicatos en SQL Server puede ser costoso y existen restricciones cuando se trabaja con tipos de datos como ntext.

Si sigue el consejo de olavk entonces su SQL para la combinación de tipos en una consulta podría terminar pareciéndose a esto:

select 
    common.id, 
    common.field-common-to-book-and-journal, 
    book.field-specific-to-book 
    journal.field-specific-to-journal 
from common-table common 
left outer join book-specific-table book on 
left outer join journal-specific-table journal on 
... etc, for each type 

He trabajado con los sistemas que utilizan los tres de estas formas y, con mucho, la vida es más fácil con una gran mesa.

+0

Estoy completamente de acuerdo. También puede agregar algunas restricciones según el tipo, para hacer columnas obligatorias para un tipo particular. Esto alivia el problema de "todo es anulable". –

+0

Mat: ¿Cómo se pueden tener contraints basados ​​en el tipo? ¿Podría hacerse eso en el DB mismo, o tendría que ser controlado por la aplicación? – robintw

+0

Supongo que los desencadenantes rechazarán la inserción de datos con campos faltantes ... –

1

Hay otra opción: no que yo había respaldo plenamente, pero sigue siendo otra opción:

Uso tres tablas:

refs (id, title, refType) 
-- title of the reference, and what type of reference it is 

fieldDef (id, fieldName, refType, dataType) 
-- name of the field, which reference types it applies to, and 
-- what type of data is stored in these fields (ISDN number, date, etc) 

fields (refId, fieldId, value) 
-- where you actually add data to the references. 

refType puede ser el tipo de referencia, y si lo hacen un entero con valores aumentados por potencias de dos (1, 2, 4, 8 ...) luego se pueden sumar para formar una máscara de bits en la tabla fieldDef.

Pros: muy simple y extensible. Si se le ocurre otro tipo de referencia, o un nuevo tipo de campo para un tipo de referencia existente, se puede agregar muy rápidamente. Los formularios se pueden generar automáticamente para cada tipo de referencia. Todos los datos se almacenan en un solo lugar, lo que significa que no necesita realizar un seguimiento de varios esquemas (¿esquemas?) para CRUD operations.

Contras: esto es lo que hace The Daily WTF. Las declaraciones seleccionadas pueden volverse muy confusas y complicadas. La base de datos no puede realizar la verificación de tipo (por ejemplo, para fechas, etc.) y el campo genérico de "valor" no se optimizará para los datos almacenados en él.

+0

Una idea muy interesante, pero puedo ver cómo conduce a TheDailyWTF. – robintw

+0

No puedo decirle cuán costosa ha sido la decisión de nuestra empresa de usar una versión extrema a principios de los años 2000. La infraestructura resultante fue comprendida por quizás 5 empleados en total. ¡El costo de oportunidad de las cosas simples que no se han hecho durante muchos años consecutivos ha sido enorme! –

+0

Derecha - ¡Definitivamente no usará esta opción! Gracias por sugerirlo. – robintw

9

"la vida es más fácil con una mesa grande": He visto la consecuencia natural de esto, ser una mesa con más de 100 columnas, y puedo decir que no me agrada trabajar con ella.

El problema principal es que los diseñadores de tales tablas tienden a omitir las restricciones necesarias para garantizar la integridad de los datos. Por ejemplo, el PO dice:

referencia

un diario requiere tanto un título de revista y un título del artículo, y también un número de página, mientras que un libro requiere un editor y una fecha de publicación, que los artículos de revistas no requieren

... lo que implica las siguientes restricciones:

CONSTRAINT a_journal_must_have_a_journal_title 
    CHECK (type <> 'journal' OR journal_title IS NOT NULL); 

CONSTRAINT a_journal_must_have_an_article_title 
    CHECK (type <> 'journal' OR article_title IS NOT NULL); 

CONSTRAINT a_journal_must_have_a_page_number 
    CHECK (type <> 'journal' OR page_number IS NOT NULL); 

CONSTRAINT a_journal_cannot_have_a_publisher 
    CHECK (type <> 'journal' OR publisher IS NULL); 

CONSTRAINT a_journal_cannot_have_a_publication_date 
    CHECK (type <> 'journal' OR publication_date IS NULL); 

CONSTRAINT a_book_cannot_have_a_journal_title 
    CHECK (type <> 'book' OR journal_title IS NULL); 

CONSTRAINT a_book_cannot_have_a_article_title 
    CHECK (type <> 'book' OR article_title IS NULL); 

CONSTRAINT a_book_cannot_have_a_page_number 
    CHECK (type <> 'book' OR page_number IS NULL); 

CONSTRAINT a_book_must_have_a_publisher 
    CHECK (type <> 'book' OR publisher IS NOT NULL); 

CONSTRAINT a_jbook_must_have_a_publication_date 
    CHECK (type <> 'book' OR publication_date IS NOT NULL); 

... y sospecho que eso es sólo la punta del iceberg!

Es mi esperanza de que después de escribir varios cientos de estas limitaciones, el diseñador puede tener dudas sobre todas esas columnas anulables :)

1

no encuentro la necesidad de unir tablas particularmente tediosas; Tomaría el enfoque más normal aquí.

0

Lo que terminé haciendo en el pasado es usar subcategorías: tener una sola tabla con todos los campos comunes, y luego varias tablas que pueden tener una relación de cero o uno con el " núcleo "mesa.

El ejemplo siguiente es similar a algo que usamos "in the wild"; Básicamente se construye una estructura de datos jerárquica, donde cada nodo puede ser una carpeta o documento:

 
CREATE TABLE Node (
    Id int identity primary key, 
    ParentId int null references Node.ParentId, 
    Name varchar(50) not null, 
    Description varchar(max) null 
) 

CREATE TABLE Doc (
    Id int primary key references Node.Id, 
    FileExtension char(3) not null, 
    MimeType varchar(50) not null, 
    ContentLength bigint not null, 
    FilePathOnDisk varchar(255) 
) 

CREATE TABLE Folder (
    Id int primary key references Node.Id, 
    ReadOnly bit not null 
) 

Así que su GetFolder sproc hará:

 
SELECT n.Id, n.ParentId, n.Name, n.Description, f.ReadOnly 
FROM Node n 
JOIN Folder f ON n.Id = f.Id 
WHERE f.Id = @Id 

Esto se traduce bastante bien en la herencia basada en la clase:

 
public class Folder : Node 
{ 
    public bool IsReadOnly { get; set; } 
    ...etc 
} 
7

Mi consejo es comenzar diseñando la base de datos correctamente, es decir, utilizando la normalización para garantizar que las tablas solo contengan datos sobre una cosa (libro, revista, etc. c.) y que los atributos se almacenan en la tabla correcta.

Si en el futuro crea problemas de rendimiento puede desnormalizarlo en menos tablas, pero es poco probable que sea un problema a menos que tenga una gran base de datos.

Cree una tabla que contendrá los atributos comunes para todas las referencias.

Cree tablas separadas para contener los atributos que son específicos para cada tipo de referencia.

El otro problema es si tendrá muchas referencias a un solo trabajo, por ejemplo. cientos de referencias a un diario en particular. La normalización sugeriría que tiene una tabla que contiene las revistas (título, autor, revista), una tabla que contiene la información de referencia que es específica de las revistas (artículo, página) y otra que contiene datos que son comunes a todas las referencias (fecha de referencia, tipo de referencia).

+0

¡Oooh! Planteó algunos problemas interesantes que no pensé. Gracias :-) – robintw

2

Mucho de lo que sería mejor depende de cuántos campos diferentes y tamaños de campo, tiene una restricción en el tamaño total de la fila (esto puede ignorarse hasta cierto punto sabiendo que todos los campos nunca serán completados, pero una vez Llegas hasta donde las páginas son demasiado anchas, el almacenamiento en realidad en la base de datos termina dividiendo la información, lo que hace que la recuperación dure más. Por lo tanto, si la información es pequeña y (esto es importante) no es probable que cambie mucho (sería un evento raro para necesitar agregar nuevo tipo de información que no se haya considerado), entonces la tabla única es la mejor, si la tabla sería demasiado amplia o si estaría sujeta a muchos cambios posibles en el tipo de datos que se deben almacenar, luego, la tabla de diálogo sería un mejor enfoque, aunque siempre será más difícil consultarla correctamente. Si a menudo desea consultar múltiples tipos de referencias al mismo tiempo, t La gran mesa es un enfoque más eficiente. Si solo necesita agarrar uno a la vez, pierde muy poco en términos de eficiencia al tener las uniones.

Si elige seguir la ruta de una tabla, asegúrese de poner activadores en la tabla que impone las reglas de integridad de datos para cada tipo de datos. Lo necesitará porque no puede confiar en que los campos sean necesarios.

Un problema con tener las tablas separadas es que no sabe hasta el tiempo de ejecución a cuál de las tablas debe unirse. Esto lo coloca en el ámbito del SQl dinámico del que no soy partidario (por razones de seguridad y eficiencia y mantenimiento) o le hace participar en tablas que puede o no necesitar, lo que es ineficiente.

Otra posibilidad es almacenar toda la cadena de referencia en un campo más grande y usar la interfaz de usuario para verificar que todas las partes necesarias estén allí antes de concatenar el registro y enviar la información a la base de datos. Esta sería la consulta más rápida para la mayoría de las consultas que desean toda la información, pero sería una molestia si necesita extraer solo algunos de los datos.También se basa en todos los datos que se insertan a través de la interfaz de usuario, que pueden o no ser el caso para usted. Honestamente, no puedo ver dónde necesitarías esta información desglosada por separado, así que este es el enfoque que probablemente tomaría. Pero no conozco las reglas de su negocio, así que tómenlo con un grano de sal.

-1

¿Qué tal ambos? ¡Tómate tu torta y cómala también!

Hay otra opción en algún lugar entre la "una gran tabla" y la base de datos "totalmente normalizada" que realmente combina lo mejor de ambos mundos: Puede usar algo llamado materialized views, que son como vistas porque son igual de flexibles y consulta tantas tablas como sea necesario, configurando todas las combinaciones, etc., pero también son como tablas en las que los resultados se almacenan realmente en una tabla.

Lo bueno de esto es que una vez que configura esto y decide cuándo se debe actualizar (cada vez que cambia una de las tablas subyacentes, o tal vez solo una vez por noche) ya no tiene que preocuparse por eso . Puede consultar la vista materializada como si fuera una gran tabla (porque lo es), y el rendimiento será rápido (más rápido que usar la instrucción de selección que está detrás). Lo que es más importante, no tiene los dolores de cabeza de mantener la integridad de los datos. Eso es lo que el DB está ahí para manejar.

Si no tiene un DB que admita esto de fábrica, puede seguir utilizando esta idea creando una tabla con los resultados de la vista como un trabajo por lotes cada noche.

0

Olavk hace buenos puntos, y Corey da una gran explicación detallada. La lectura de la información de Corey, sin embargo, me da una conclusión de la respuesta de Olavk. Tenga en cuenta que, dependiendo de lo que esté haciendo con la información, puede terminar 2 etapas de su consulta. Encuentre el artículo, luego, para cada referencia, haga una selección directa de lo que le interese.

Considere también la idea de almacenar todo en varias tablas y leerlo desde una sola tabla. Lo hago para una gran base de datos que tengo, donde la mayoría de las consultas necesitan cierta información común, pero todavía se requiere el diseño completo de varias tablas. Las inserciones se ralentizan un poco por los desencadenantes que inician (en mi caso, uno por archivo donde cada archivo es responsable de hasta un millón de filas insertadas), pero mis últimas consultas de selección pueden ir de minutos a segundos de un solo dígito.

El almacenamiento de datos :)

0

tuve una discusión sobre estos temas hace algún tiempo con mi superior. Por supuesto, no pude probar que "enfoque jerárquico de varias mesas" (vea olavk's answer) es mejor, ¡pero lo sentí! Yo siempre elegiría este método. Una tabla raíz con todos los campos que las entidades tienen en común, y 1-1 tablas secundarias con campos que no tienen en común. Si es necesario, este enfoque puede extenderse a más tablas secundarias, siempre que la lógica de negocios y otras entidades tengan algo fuera de él. Es decir, no creo que haya que ir por la borda con esto.

También estoy en contra de la creación de tablas "secundarias" separadas sin la tabla raíz, donde cada tabla tiene una copia de los mismos campos. Creo que Corey's answer sugiere este enfoque como un ejemplo de un mal modelo multi-mesa, y también lo critica. Me gustaría agregar que tener que escribir uniones no es el problema principal con eso. No es un problema en absoluto, ya que la mayoría de las consultas de bases de datos tienen muchas combinaciones, y es algo normal. Es difícil crear relaciones con otras tablas: siempre necesita un ID y un TypeId para saber qué tabla está vinculada a él. En el caso de una tabla raíz, solo necesita el Id.

Cuestiones relacionadas