Foros del Web » Programando para Internet » PHP »

Problema para hayar coincidencias en una tabla

Estas en el tema de Problema para hayar coincidencias en una tabla en el foro de PHP en Foros del Web. Hola!! Estoy creando una web de viajes, donde los usuarios pueden votar con puntuaciones numéricas las zonas que ha visitado. La idea es, aposteriori, intentar ...
  #1 (permalink)  
Antiguo 07/07/2011, 17:30
 
Fecha de Ingreso: octubre-2007
Mensajes: 68
Antigüedad: 16 años, 6 meses
Puntos: 6
Problema para hayar coincidencias en una tabla

Hola!! Estoy creando una web de viajes, donde los usuarios pueden votar con puntuaciones numéricas las zonas que ha visitado.

La idea es, aposteriori, intentar crear una formula que pueda buscar "su alma gemela" basándose en las puntuaciones que haya puesto. La idea es compararlas y en base a esas comparaciones darle un % de coincidencia.

He diseñado lo siguiente, basándome en que para comparar, el usuario debe tener como mínimo 30 puntuaciones

Código PHP:
<?
    $sujeto1
=$_GET["usuario"]; //este es el nombre del usuario que busca su alma gemela
    
$resultnick=mysql_query("select count(puntuacion)as total, usernick from puntos_paisaje group by usernick HAVING count(puntuacion) > 30"); //empezamos a sacar los nombres de los demas usuarios que tengan como mínimo 30 puntuaciones
    
while ($rownick=mysql_fetch_array($resultnick))
        {
            
$sujeto2=$rownick["usernick "];
            
$porcentaje=0;
            
$coinciden=0;
            
$result=mysql_query("SELECT usernick , puntuacion,idlugar as idl, (select puntuacion  from puntos_paisaje where idlugar=idl and usernick='$sujeto2') as notal_sujeto2 from puntos_paisaje where usernick='$sujeto1'");

//con esto saco todas las puntuaciones del sujeto 1, y al lado las del sujeto 2
            
while ($row=mysql_fetch_array($result))
                {
                
$puntuacion=$row["puntuacion"];
                    if    (
$row["notal_sujeto2"]<>NULL// pregunto si el sujeto 2 tiene puntuada ese paisaje
                        
{
                        ++
$coinciden//si coincide aumento en 1 el contador de coincidencias
                        
$operador=$row["notal_sujeto2"]-$puntuacionrecto la nota del sujeto 1 con la del sujeto 2
                        
switch($operador)  //compruebo el resultado de la recta 
                           

                           case 
0:$porcentaje=$porcentaje+2; break;  // si es cero es que coinciden la nota, le sumo 2 puntos
                           
case 1:$porcentaje=$porcentaje+1; break;  //si tiene una difrencia de uno le sumo 1 punto
                           
case -1:$porcentaje=$porcentaje+1; break; //si tiene una difrencia de uno le sumo 1 punto
                           
case 2:$porcentaje=$porcentaje+0.5; break;  //si tiene una difrencia de uno entre 2 y 3 punto le sumo 0,5puntos
                           
case -2:$porcentaje=$porcentaje+0.5; break; 
                           case 
3:$porcentaje=$porcentaje+0.5; break; 
                           case -
3:$porcentaje=$porcentaje+0.5; break; 
                           case 
4:$porcentaje=$porcentaje-1; break;  //si hay una difrencia de 4 puntos o mas le recto 1
                           
case -4:$porcentaje=$porcentaje-1; break; 
                           case 
5:$porcentaje=$porcentaje-1; break; 
                           case -
5:$porcentaje=$porcentaje-1; break; 
                           case 
6:$porcentaje=$porcentaje-1; break; 
                           case -
6:$porcentaje=$porcentaje-1; break; 
                           case 
7:$porcentaje=$porcentaje-1; break; 
                           case -
7:$porcentaje=$porcentaje-1; break; 
                           case 
8:$porcentaje=$porcentaje-1; break; 
                           case -
8:$porcentaje=$porcentaje-1; break; 
                           case 
9:$porcentaje=$porcentaje-1; break; 
                           case -
9:$porcentaje=$porcentaje-1; break; 
                           case 
10:$porcentaje=$porcentaje-1; break; 
                           case -
10:$porcentaje=$porcentaje-1; break; 
                            }    
                        }
                
                }
            
$operador=$porcentaje/($coinciden*2)*100//el porcentaje
            
$tabla[$k][0]=$operador// lo guardo en un array el porcentaje de afinidad
            
$tabla[$k][1]=$sujeto2// nombre del sujeto
            
$tabla[$k][2]=$coinciden//coincidencias
            
++$k;
        }
rsort($tabla); //ordeno por el porcentaje de afinidad
$k=0;
while(
$k<20)
    {
    echo 
'<br><br>El usuario'.$tabla[$k][1].' tiene una afinidad de '.$tabla[$k][0].'  (Total de conicidiencias  '.$tabla[$k][2].')';
    ++
$k;
    }
?>


Esto funciona, el problema es que en realizar esta operación tarda aproximadamente 5 minutos, por la cantidad de usuarios y puntuaciones que hay ...


¿podéis echarme una mano en como conseguir que este código sea optimo?

La verdad es que llevo poco tiempo programando, y lo he aprendido de forma autodidacto, con lo que intuyo que habrá muchos fallos que podrían optimizar este tipo de consultas

gracias
  #2 (permalink)  
Antiguo 07/07/2011, 18:08
 
Fecha de Ingreso: marzo-2011
Mensajes: 68
Antigüedad: 13 años, 1 mes
Puntos: 9
Respuesta: Problema para hayar coincidencias en una tabla

Seguramente es la segunda consulta. No me fije exactamente que hace, pero por lo que vi seguro se puede hacer mejor =P.
Por otro lado en vez de buscar por el nombre de usuario tendrías que hacer un campo ID que sea primary (se buscan mucho más rápido los números que las cadenas), o por lo menos hacer que el nombre de usuario sea UNIQUE.

Saludos!
  #3 (permalink)  
Antiguo 08/07/2011, 01:29
 
Fecha de Ingreso: octubre-2007
Mensajes: 68
Antigüedad: 16 años, 6 meses
Puntos: 6
Respuesta: Problema para hayar coincidencias en una tabla

Cita:
Iniciado por FFabre Ver Mensaje
Seguramente es la segunda consulta. No me fije exactamente que hace, pero por lo que vi seguro se puede hacer mejor =P.
Por otro lado en vez de buscar por el nombre de usuario tendrías que hacer un campo ID que sea primary (se buscan mucho más rápido los números que las cadenas), o por lo menos hacer que el nombre de usuario sea UNIQUE.

Saludos!
El problema es que la primera consulta tiene 900 resultados que tienen que pasar por la otra consulta "compleja".

Supongo que realizar 900 consultas en una ejecución es una salvajada, pero no encuentro una forma que me permite reducirlas

  #4 (permalink)  
Antiguo 08/07/2011, 04:55
Avatar de vgonga1986  
Fecha de Ingreso: marzo-2008
Ubicación: País de Pandereta
Mensajes: 1.021
Antigüedad: 16 años, 1 mes
Puntos: 253
Respuesta: Problema para hayar coincidencias en una tabla

Buenas,

El principal problema que veo es la construcción de la base de datos. Por lo que veo en la consulta que has puesto, tienes una tabla llamada puntos_paisaje que tiene un registro por cada usuario y cada lugar, con la puntuación que le ha asignado. Además, guardas el nick del usuario. Las bases de datos relacionales están optimizadas para consultas de campos llave.

Lo suyo sería crear tres tablas, Paisaje, Usuario y Puntuación. En Paisaje pones un id_paisaje y en usuario un id_usuario. En Puntuanción pones sólo cuatro columnas, id_puntuación (clave primaria), id_paisaje (clave externa), id_usuario (clave externa) y puntuación. Aunque las consultas se van complicando, ya que tienes que hacer consultas a varias tablas en muchas ocasiones, la ganancia de optimización en las búsquedas es brutal y compensa sobremanera.

Si no tienes nociones de bases de datos, no te preocupes, la experiencia te va dando las pistas. Pero te recomendaría que echaras un vistazo a algún manual de diseño de bases de datos, al fin y al cabo son la base de las aplicaciones y el mejor sitio para empezar a optimizar código.

Un saludo, espero que te sirva de algo.
  #5 (permalink)  
Antiguo 08/07/2011, 06:55
 
Fecha de Ingreso: octubre-2007
Mensajes: 68
Antigüedad: 16 años, 6 meses
Puntos: 6
Respuesta: Problema para hayar coincidencias en una tabla

Cita:
Iniciado por vgonga1986 Ver Mensaje
Buenas,

El principal problema que veo es la construcción de la base de datos. Por lo que veo en la consulta que has puesto, tienes una tabla llamada puntos_paisaje que tiene un registro por cada usuario y cada lugar, con la puntuación que le ha asignado. Además, guardas el nick del usuario. Las bases de datos relacionales están optimizadas para consultas de campos llave.

Lo suyo sería crear tres tablas, Paisaje, Usuario y Puntuación. En Paisaje pones un id_paisaje y en usuario un id_usuario. En Puntuanción pones sólo cuatro columnas, id_puntuación (clave primaria), id_paisaje (clave externa), id_usuario (clave externa) y puntuación. Aunque las consultas se van complicando, ya que tienes que hacer consultas a varias tablas en muchas ocasiones, la ganancia de optimización en las búsquedas es brutal y compensa sobremanera.

Si no tienes nociones de bases de datos, no te preocupes, la experiencia te va dando las pistas. Pero te recomendaría que echaras un vistazo a algún manual de diseño de bases de datos, al fin y al cabo son la base de las aplicaciones y el mejor sitio para empezar a optimizar código.

Un saludo, espero que te sirva de algo.

Hola, gracias por tu respuesta.

Ahora mismo existen tres tablas:

tabla_usuarios (campo primario es el nombre del nick que es unico)
tabla_viajes (campo primerio id_viaje)
tabla_puntos (campo primario id_punto, nick, id_viaje y puntos)

Lo unico que nos la tengo relacionadas, simplemente lo controlo con campos unicos y pregunto por ellos en condiciones where. No se si eso es lo correcto o es mejor usar relaciones en este caso.

El problema que veo en lo que os he planteado es el número de consultas, pues de cada usuario que hay en la base de datos tiene que contrastar puntuacion por puntacion para ver si coincide o si hay diferencia.

Al final se generam más de 1000 consultas de una tirada, y supongo que eso no es "optimo", pero no se como hacer una única o intentar que no sean tal barbaridad de numero.


Supongo que optimizar las tablas ganaría más, pero aun asi siendo casi 1000 consultas no tendré el mismo problema? No sería en este caso lo fundamental evitar tener que hacer tantas consultas?

Gracias :)
  #6 (permalink)  
Antiguo 08/07/2011, 08:14
 
Fecha de Ingreso: marzo-2011
Mensajes: 68
Antigüedad: 13 años, 1 mes
Puntos: 9
Respuesta: Problema para hayar coincidencias en una tabla

Si, totalmente. Mostrame exactamente el diseño de tu base de datos, capaz que cambiandolo sería mejor. O haciendo un join en vez de tantas consultas.

De todas formas, te recomiendo que no reconozcas a cada usuario por su nick si no que les des un ID, eso va a hacer que sea más fácil (rápido) buscar coincidencias.

Saludos!
  #7 (permalink)  
Antiguo 08/07/2011, 08:33
 
Fecha de Ingreso: octubre-2007
Mensajes: 68
Antigüedad: 16 años, 6 meses
Puntos: 6
Respuesta: Problema para hayar coincidencias en una tabla

Tabla lugares
---------------
idlugar
nombre
ciudad
...



Tabla usuario
---------
usernick
email
pais
ciudad
passwrd
....



tabla puntos_paisaje
----------------------------
id_puntuacion
idlugar
usernick
puntos



Esto es la estructura básica que está implicada, pero como te decía no tienen relaciones, simplemente lso campos subrayados son claves e unicos. En verdad todas las consultas (como puedes ver en el codigo que puse antes) se hacen sobre puntos_paisaje.

Lo que necesito es saber que usuarios puntuan los paisajes lo más parecido posible, para ello sigo el siguiente criterio:

Si coincide la votacion le doy 2 puntos
Si difiere 1 punto (por arriba o por abajo) le doy 1 punto
Si difiere 2 o 3 puntos (por arriba o por abajo) le doy 0.5 puntos
Si difere 4 le recto -0.5 puntos
Si difiere más de 4 le recto -1 punto

El resultado se divide por el nº de paisajes que han coincidido en puntuar, lo multiplico por 100 y ya tengo el porcentaje

EL problema es que para ello necesito que yo como usuario, la base de datos contracte TODAS LAS NOTAS suyas con TODAS LAS PUNTUACIONES DE CADA UNO DE LOS USUARIOS de la base de datos. Para que sea efectivo esto lo limito a 50, es decir que para que un usuario sea analizado tiene que haber puntuado al menos 50 paisajes.


El problema es que 900 usuarios con más de 50 puntuaciones, eso multiplica TODAS LAS PUNTUACIONES del usuario que busca a los usuarios coincidentes por los 900 usuarios multipolicado por sus puntuaciones

Esto genera (al menos como lo tengo diseñado másd e 1000 consultas y no se como hacer para reducilo ya sea con inner join left join o cualquier otra formala

Etiquetas: coincidencias, tabla
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 21:53.