2010-05-27 10 views
7

Tengo una tabla que almacena comentarios, el comentario puede provenir de otro usuario u otro perfil que son entidades separadas en esta aplicación.Mejores prácticas de base de datos

Mi pensamiento original era que la mesa tendría ambos campos user_id y profile_id, por lo que si un usuario envía un comentario, se da la user_id deja el profile_id blanco

es este derecho, mal, ¿hay una manera mejor ?

Respuesta

1

En el pasado he usado una tabla de comentarios centralizada y tenía un campo para la tabla fk a la que hace referencia.

por ejemplo:

comments(id,fk_id,fk_table,comment_text) 

esa manera se puede utilizar consultas UNION para concatenar los datos de diversas fuentes.

SELECT c.comment_text FROM comment c JOIN user u ON u.id=c.fk_id WHERE c.fk_table="user" 
UNION ALL 
SELECT c.comment_text FROM comment c JOIN profile p ON p.id=c.fk_id WHERE c.fk_table="profile" 

Esto garantiza que puede ampliar el número de objetos que tienen comentarios sin crear tablas redundantes.

4

Si los comentarios son generales para varios objetos que podrían crear una tabla para cada objeto:

user_comments (user_id, comment_id) 
profile_comments (profile_id, comment_id) 

entonces usted no tiene que tener ningún columnas vacías en la tabla de comentarios. También hará que sea más fácil agregar nuevos objetos fuente de comentarios en el futuro sin tocar la tabla de comentarios.

+0

+ 1 para la técnica correctamente normalizada – DancesWithBamboo

+0

Me gusta esta solución, pero hacer un simple filtrado de los comentarios por comentarista o tipo de comentarista requeriría una unión. – cherouvim

+0

De hecho, pero una unión es una pequeña desventaja, y siempre es posible crear una vista o un procedimiento almacenado para esto, por lo que no tiene que pensar en las uniones cada vez que necesite obtener esos datos. – becquerel

3

Otra forma de resolver es siempre desnormalizar (copiar) el nombre del comentador en el comentario y también almacenar una referencia de regreso al comentarista a través de un tipo y un campo de identificación. De esta forma, tiene una tabla de comentarios unificada donde puede buscar, clasificar y recortar rápidamente. El inconveniente es que no existe una relación FK real entre un comentario y su propietario.

+0

De los métodos mencionados hasta ahora, prefiero este. Sí, no hay una relación de clave extranjera real; eso siempre será un inconveniente, ¡pero no muchas aplicaciones web se preocupan por eso! Esta es la forma más extensible sin crear muchas tablas diferentes que almacenen información similar. –

+0

No entiendo. ¿Estás diciendo que guardes una copia del nombre de los comentaristas pero no FK? – Tim

+0

@ user270797: los datos de comentarios ya están sobre la mesa. Los datos del comentarista (nombre, correo electrónico, etc.) deben copiarse (desnormalizarse) en la tabla de comentarios para facilitar el acceso. Esta solución tiene sentido en algunos escenarios. – cherouvim

5

Cualquiera que sea la mejor solución depende en mi humilde opinión de más que solo la tabla, sino también cómo se utiliza en otra parte de la aplicación.

Suponiendo que todos los comentarios están asociados con algún otro objeto, digamos que extrae todos los comentarios de ese objeto. En su diseño propuesto, extraer todos los comentarios requiere seleccionar de una sola tabla, que es eficiente. Pero eso es extraer los comentarios sin extraer la información sobre el póster de cada comentario. Quizás no quieras mostrarlo, o tal vez ya estén en la memoria.

Pero, ¿y si tuvieras que recuperar información sobre el póster mientras recuperas los comentarios? Luego tiene que unirse a dos tablas diferentes, y ahora el conjunto de registros resultante se está contaminando con una gran cantidad de valores NULL (para un comentario de perfil, todos los campos de usuario serán NULL). El código que tiene que analizar este conjunto de resultados también podría volverse más complejo.

En lo personal, yo probablemente comenzar con la versión totalmente normalizado, y luego desnormalizar cuando empiece a ver los problemas de rendimiento

También hay una solución completamente diferente posible al problema, pero esto depende de si es o no hace sentido en el dominio. ¿Qué pasa si hay otros lugares en la aplicación donde un usuario y un póster se pueden usar de manera intercambiable? ¿Qué pasa si un usuario es solo un tipo especial de perfil? Entonces creo que la solución debería resolverse generalmente en las tablas de usuario/perfil.Por ejemplo (algunos abreviados pseudo-SQL):

create table AbstractProfile (ID primary key, type) -- type can be 'user' or 'profile' 
create table User(ProfileID primary key references AbstractProfile , ...) 
create table Profile(ProfileID primary key references AbstractProfile , ...) 

Entonces, cualquier lugar en su aplicación, donde un usuario o un perfil se pueden utilizar indistintamente, puede hacer referencia al LoginID.

+1

+1 Esta es la solución que comencé a escribir. Ver también * Asociaciones polimórficas * en http://www.slideshare.net/billkarwin/practical-object-oriented-models-in-sql –

0

Aquí hay otro enfoque, que le permite mantener la integridad referencial a través de claves externas, gestionar de forma centralizada, y proporciona el más alto rendimiento utilizando herramientas de bases de datos estándar, tales como índices y si realmente necesita, etc partición:

create table actor_master_table(
    type char(1) not null, /* e.g. 'u' or 'p' for user/profile */ 
    id varchar(20) not null, /* e.g. 'someuser' or 'someprofile' */ 
    primary key(type, id) 
); 

create table user(
    type char(1) not null, 
    id varchar(20) not null, 
    ... 
    check (id = 'u'), 
    foreign key (type, id) references actor_master_table(type, id) 
); 

create table profile(
    type char(1) not null, 
    id varchar(20) not null, 
    ... 
    check (id = 'p'), 
    foreign key (type, id) references actor_master_table(type, id) 
); 

create table comment(
    creator_type char(1) not null, 
    creator_id varchar(20) not null, 
    comment text not null, 
    foreign key(creator_type, creator_id) references actor_master_table(type, id) 
);