messianos / pci_server

1 stars 1 forks source link

Solution debe conocer si es aceptada o no? #14

Open messianos opened 11 years ago

messianos commented 11 years ago

Estaba tratando de contar la cantidad de accepted_solution de un user y me encontre con que hacer la consulta para eso es bastante rebuscada e ineficiente.

Tenemos:

Solution(id, ...., username) Problem(id, accepted_solution, ...) problem_solutions(problem_id, solution_id)

para obtener las soluciones aceptadas habria q hacer:

accepted_solution => filter Solutions by the user => join con problem_solutions => join con Problem => filter Problem.accepted_solution_id = Solution.id

no es mucho? lo vamos a usar en el ranking y en el perfil. Ni hablar de listar las accepted_solutions y ponerle el link al problema.

No seria mejor poner un "id_of_problem_solved_by_this" y actualizarlo cada vez que se actualiza "accepted_solution_id" de Problem? En ese caso el count seria:

SELECT count(*) FROM Solution WHERE creator_user_name = 'username' AND id_of_problem_solved_by_this NOT NULL

Mucho mas rapida...

GNZ commented 11 years ago

Si además que tenemos varios accesos a disco (y el procesamiento). Creo que tiene sentido, además la actualización del "accepted_solution_id" no creo que sea muy recurrente.

federicojasson commented 11 years ago

Típico trade-off entre espacio y tiempo de ejecución.

Ventajas: -Las búsquedas son más rápidas.

Desventaja: -Un "puntero" extra por cada solución.

Creo que vale la pena en este caso agregar ese campo.

De todas formas planteo una alternativa de diseño para que la pensemos:

En lugar de tener un campo accepted_solution_id en Problem y un campo id_of_problem_solved_by_this en Solution, se podría agregar una tabla que mantenga ambos pares (llámese por el momento solved_problem).

La búsqueda sería

SELECT count(*) FROM Solution JOIN solved_problem WHERE Solution.creator_user_name LIKE BINARY 'user_name'

La búsqueda de los IDs de los problemas resueltos por un cierto usuario se obtendría como:

SELECT Problem.id, Problem.description FROM Solution JOIN solved_problem JOIN Problem WHERE Solution.creator_user_name LIKE BINARY 'user_name'

Ventajas: -Evitás un campo extra en cada Solution y en cada Problem (que en general va a ser NULL). -La cantidad de pares en solved_problem va a ser más bien menor. Esto puede ser eficiente a la hora de hacer JOIN (?).

Desventajas: -Requiere un JOIN por cada búsqueda.

Aclaro que en la otra alternativa, para obtener los "links" a los problemas resueltos por un cierto usuario también necesitás hacer un JOIN (porque en general no te va a bastar con obtener el ID del problema resuelto, sino que se va a requerir también su descripción por una cuestión de elegancia). Este JOIN sería entre las tablas Problem y Solution directamente (haciendo coincidir Problem.accepted_solution_id con Solution.id). Puedo estar equivocado, pero ¿esto no es MUY ineficiente? ¿Hacer Solution JOIN solved_problem JOIN Problem es más eficiente? (habría que leer la documentación y ver si en este caso el JOIN se hace en el orden planteado, es decir, primero haciendo el JOIN de una tabla grande con una chica y después de la tabla resultante con la otra grande).

En fin, esto es hilar fino. Puede que no valga la pena.

UNA COSA MÁS IMPORTANTE: estaría bueno ir anotando en un archivo en el directorio doc/ los cambios propuestos, para hacerlos todos a la vez. Teniendo en cuenta que la base de datos está en el hosting, cambiarla va a ser más "difícil". Por el diseño que hicimos, el único cambio que hay que hacer es en DatabaseInterface. Por ahora podemos implementar la solución ineficiente con el diseño actual, y después cambiarlo.