2009-05-07 14 views
5

Estoy buscando diseñar una base de datos para un sitio web donde los usuarios puedan obtener puntos (reputación) para realizar ciertas actividades y estoy luchando con el diseño de la base de datos.¿Cuál es la mejor manera de almacenar/calcular los puntajes de los usuarios?

Estoy planeando mantener registros de las cosas que hace un usuario para que puedan tener 25 puntos para un artículo que han enviado, 1 punto cada uno por 30 comentarios que han hecho y otros 10 puntos de bonificación por ser increíble!

Está claro que todos los datos estarán allí, pero parece que es mucho o complicado obtener el puntaje total de cada usuario que me gustaría mostrar junto a su nombre de usuario (en forma de un nivel). Por ejemplo, una consulta a la tabla de elementos enviados para obtener los puntajes de cada elemento de ese usuario, una consulta a la tabla de comentarios, etc. Si todo esto tiene que hacerse para cada usuario mencionado en una página ... MUCHAS consultas !

Había considerado mantener un puntaje en la tabla de usuarios, lo que parecería mucho más rápido de buscar, pero me han convencido de que almacenar datos que se pueden calcular a partir de otros datos es ¡MALO!

He visto una gran cantidad de sitios que hacen cosas similares (incluso el desbordamiento de pila hace algo similar), así que creo que debe haber una "mejor práctica" a seguir. ¿Alguien puede sugerir cuál puede ser?

Cualquier sugerencia o comentario sería genial. ¡Gracias!

Respuesta

3

Creo que esta es definitivamente una gran pregunta. He tenido que crear sistemas que tengan un comportamiento similar a este, especialmente cuando se accede a la tabla con los puntajes con bastante frecuencia (como en su escenario).Aquí está mi sugerencia para usted:

En primer lugar, crear algunas tablas como la siguiente (estoy usando las mejores prácticas de SQL Server, pero nombrarlos sin embargo le parezca):

UserAccount   UserAchievement 
-Guid (PK)   -Guid (PK) 
-FirstName   -UserAccountGuid (FK) 
-LastName   -Name 
-EmailAddress  -Score 

Una vez que haya hecho esto , seguir adelante y crear una vista que se ve algo como lo siguiente (no, no he verificado este SQL, pero debe ser un buen comienzo):

SELECT [UserAccount].[FirstName]  AS FirstName, 
     [UserAccount].[LastName]  AS LastName, 
     SUM([UserAchievement].[Score]) AS TotalPoints 
FROM [UserAccount] 
INNER JOIN [UserAchievement] 
    ON [UserAccount].[Guid] = [UserAchievement].[UserAccountGuid] 
GROUP BY [UserAccount].[FirstName], 
     [UserAccount].[LastName] 
ORDER BY [UserAccount].[LastName] ASC 

sé que has mencionado alguna preocupación por el rendimiento y muchas consultas, pero si construyes una vista como esta, nunca necesitarás m más de uno. Recomiendo no hacer de esto una vista materializada; en su lugar, simplemente indexe sus tablas para que las búsquedas que necesite (esencialmente, UserAccountGuid) permitan una suma rápida en toda la tabla.

Añadiré un punto más: si su tabla UserAccount se vuelve enorme, puede considerar una consulta un poco más inteligente que incorpore los nombres de las cuentas para las que necesita obtener roll-ups. Esto posibilitará no devolver enormes conjuntos de datos a su sitio web cuando solo muestra, 3-10 información de los usuarios en la página. Tendría que pensar un poco más acerca de cómo hacer esto elegantemente, pero sugiero que se mantenga alejado de las declaraciones "IN" ya que esto invocará una búsqueda lineal de la tabla.

1

Para relaciones de lectura/escritura muy altas, la desnormalización es una opción muy válida. Puede utilizar una vista indizada y los datos se mantendrán sincronizados de forma declarativa (para que nunca tenga que preocuparse de que haya datos incorrectos). El inconveniente es que se mantiene sincronizado ... por lo que las actualizaciones del total de la tienda son un aspecto sincrónico de la ejecución de la acción de puntaje. Esto normalmente sería bastante rápido, pero es una decisión de diseño. Si se desnormaliza, puede elegir si desea tener algún tipo de sistema de actualización diferida.

Personalmente, me gustaría ir con una vista indizada para comenzar, y luego puede reemplazarlo bastante a la perfección con una tabla de concreto si así lo requieren.

+0

Lo siento, soy tonto, no noté el aspecto de mysql. No creo que tenga vistas indexadas todavía. La alternativa normal para obligarlo a mantenerse sincronizado es configurar los desencadenantes. – ahains

+0

Los gatillos hacen llorar al bebé Jesús. Bueno, me hacen llorar, de todos modos. Incluso si no está indexado, una vista es tu amigo aquí. – Adrien

0

En el pasado siempre usamos algún tipo de trabajo cron nocturno o peródico para calcular el puntaje actual y guardarlo en la base de datos, como una vista persistente de SUM en la tabla de actividades. Al igual que la mayoría de las "mejores prácticas", son simplemente pautas y, a menudo, es mejor y más práctico desviarse de una práctica concreta específica en áreas muy específicas.

Además, no es una desviación tan grande si utiliza el trabajo cron, ya que se ve mejor como un caché almacenado en la base de datos.

0

Si tiene una tabla de puntajes por separado, puede actualizarla cada vez que se envía un artículo o un usuario publica un comentario. Puedes hacer esto usando un disparador o dentro del código de sitios.

Los puntajes de los usuarios se actualizarían continuamente y podrían consultarse rápidamente para su visualización.

Cuestiones relacionadas