Foros del Web » Programación para mayores de 30 ;) » Bases de Datos General » Mysql »

[SOLUCIONADO] Query que cuelga el ordenador (para Búsqueda Avanzada)

Estas en el tema de Query que cuelga el ordenador (para Búsqueda Avanzada) en el foro de Mysql en Foros del Web. Hola a todos. Tengo un problema al intentar programar la Búsqueda Avanzada de mi web. Tengo las siguientes tablas en mi BBDD (que, según entiendo, ...
  #1 (permalink)  
Antiguo 13/07/2015, 05:02
 
Fecha de Ingreso: julio-2008
Mensajes: 84
Antigüedad: 16 años, 5 meses
Puntos: 0
Query que cuelga el ordenador (para Búsqueda Avanzada)

Hola a todos. Tengo un problema al intentar programar la Búsqueda Avanzada de mi web.

Tengo las siguientes tablas en mi BBDD (que, según entiendo, es la correcta conforme a las Formas de Normalización):

items, que contiene los objetos a buscar (80.000 registros).

atributo_1, que contiene el listado de los posibles valores del atributo 1 (1.000 registros)
interrelacion_1, relaciona el ID del ítem con los valores que tiene para el atributo 1, que pueden ser 0, 1, 2, etc (3.000 registros)
atributo_2, que contiene el listado de los posibles valores del atributo 2 (10.000 registros)
interrelacion_2, relaciona el ID del ítem con los valores que tiene para el atributo 2, que pueden ser 0, 1, 2, etc (30.000 registros)
...
atributo_7, que contiene el listado de los posibles valores del atributo 7 (5.000 registros)
interrelacion_7, relaciona el ID del ítem con los valores que tiene para el atributo 7, que pueden ser 0, 1, 2, etc (9.000 registros)

La query que uso para realizar la búsqueda es la siguiente.
El problema es que cuando lanzo la query se me cuelga el ordenador.

Código MySQL:
Ver original
  1. FROM items, atributo_1, interrelacion_1 , atributo_2,  interrelacion _2, ...,  atributo_7,   interrelacion _7
  2. WHERE items.item_id = interrelacion_1.item_id
  3. AND interrelacion_1.type_id = atributo_1.type_id
  4. AND items.item_id = interrelacion _2.item_id
  5. AND interrelacion _2.genre_id = atributo_2.genre_id
  6. ...
  7. AND items.item_id = interrelacion _7.item_id
  8. AND interrelacion _7.genre_id = atributo_7.siete_id
  9. AND items.item_name_en LIKE CONCAT('%','aaa','%')
  10. AND atributo_1.type_id != -1
  11. AND atributo_2.genre_id != -1
  12. ...
  13. AND atributo_7.siete_id != -1
  14. AND items.rating >= 0
  15. AND items.issue_year >= 1900
  16. AND items.issue_year <= 3000



Gracias de antemano por vuestra ayuda.

Última edición por gnzsoloyo; 13/07/2015 a las 05:34
  #2 (permalink)  
Antiguo 13/07/2015, 05:45
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: Query que cuelga el ordenador (para Búsqueda Avanzada)

Y... hay muchas cosas.

Por empezar:
Cita:
Tengo las siguientes tablas en mi BBDD (que, según entiendo, es la correcta conforme a las Formas de Normalización):
En realidad no lo sabemos. Tendríamos que confiar en tus capacidades y tu palabra, pero por las experiencias que hemos tenido en el foro...
Como sea, sin verlo no estaremos seguros si está realmente como dices.

Luego:
Código MySQL:
Ver original
  1. AND atributo_1.type_id != -1
  2. AND atributo_2.genre_id != -1
  3. ...
  4. AND atributo_7.siete_id != -1
Es un asco de performance. La única forma en que se puede validar algo "diferente a " un valor discreto es comparando todos y cada uno de los registros, lo que implicaría un full table scan sobre el resultado de los JOINs.

Además:
Código MySQL:
Ver original
  1. AND items.item_name_en LIKE CONCAT('%','aaa','%')
Esta es una de esas condiciones decididamente espantosas
Esa condición, al DBMS le dice "lee TODOS los registros y buscá eso en cualquier parte de ese campo en cada registro".
No se recomienda usar los LIKE con comodines a ambos lados. Además, si el valor "aaa" entrase por variable, deberías asegurarte que la longitud de la palabra a buscar no sea menor a cinco letras, o se volverá MUY ineficiente.
Código MySQL:
Ver original
  1. AND items.issue_year >= 1900
  2. AND items.issue_year <= 3000
Si vas a buscar un rango, usa BETWEEN. En MySQL es ineficiente usar formas como esa.

Finalmente: Sin conocer la selectividad de cada tabla y de cada condición es imposible saber con certeza qué tan bien están armados los JOIN, porque en caso de no ser de buena selectividad estarías haciendo una forma de producto cartesiano.
Habría que realizar algunas pruebas con las tablas reales (me parece que estás dandonos nombres supuestos, lo que es mala práctica según las reglas del foro, que te sugiero leer), y con suposiciones o simulaciones las soluciones nunca son buenas..

Para que te des una idea, si la relación fuese exactamente la que dices, (80 000 * 1 000 * 1 000 * 5 000 * 9 0000), el total de registros en un producto cartesiano rondaría los 108.000.000.000.000.000.000.000...
TE conviene analizar antes que nada la performance de la query con el uso de EXPLAIN. Eso te dará una aproximación acerca de dónde falla tu consulta.

Explicanos qué es lo que intentaste para resolverlo y veamos qué cosas ya probaste. Asi no volvemos sobre los senderos caminados.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #3 (permalink)  
Antiguo 13/07/2015, 10:14
 
Fecha de Ingreso: julio-2008
Mensajes: 84
Antigüedad: 16 años, 5 meses
Puntos: 0
Respuesta: Query que cuelga el ordenador (para Búsqueda Avanzada)

Muchas gracias por contestar gnzsoloyo.

A ver, ea query es solo un ejemplo que recoge campos para ver qué hay en base de datos. Ya sé que no está bien, pero era solo demostrar que una consulta así no puede hacer que se vuelgue el ordenador (que tarde sí, que casque el ordenador no).

El ejemplo completo que hace que falle sería este.
Esta consulta saca los generos de unos items (hay unos 1000 géneros y la tabla que relaciona géneros con items tiene unos 70.000 registros)

Código MySQL:
Ver original
  1. SELECT COUNT(i.item_id)
  2. FROM items i, item_tipos it, item_tipos_asociados ita
  3. WHERE i.item_id = ita.item_id
  4. AND ita.type_id = it.type_id
  5. AND it.type_id = 3

Saco los items con el género 3. Hay unos 300 géneros y unos 50.000 relaciones de género-item. Esto devuelve 50 resultados.

Lo mismo para categorías:
Código SQL:
Ver original
  1. SELECT COUNT(i.item_id)
  2. FROM items i, items_categorias ic, items_categorias_asociados icd
  3. WHERE i.item_id = icd.item_id
  4. AND icd.item_category_id = ic.item_category_id
  5. AND ic.item_category_id = 3

Y para las categorias 3. Hay unos 100 géneros y unos 80.000 relaciones de categorias-item. hay 10 reultados

Lo mismo con publishers (60 publishers y 30.000 resultados de relacion publisher-item) y developer (400 developer y 20.000 resultados de relación developer-item):

Código MySQL:
Ver original
  1. SELECT COUNT(i.item_id)
  2. FROM items i, item_publishers ip, item_publisher_asociados ipa
  3. WHERE i.item_id = ipa.item_id
  4. AND ipa.publisher_id = ip.publisher_id
  5. AND ip.publisher_id = 3

Código MySQL:
Ver original
  1. SELECT COUNT(i.item_id)
  2. FROM items i, item_developers id, item_developer_asoc ida
  3. WHERE i.item_id = ida.item_id
  4. AND ida.developer_id = id.developer_id
  5. AND id.developer_id = 3;

Con 40 resultados el primero y 20 el segundo.

Si ahora hago un union de todas las consultas, sólo para ver lo que tarda, ya sé que con solo un union se repiten resultados y no es correcta. Es sólo para ver lo que tarda:
Código MySQL:
Ver original
  1. SELECT COUNT(i.item_id)
  2. FROM items i, item_generos ig, item_generos_asociados iga
  3. WHERE i.item_id = iga.item_id
  4. AND iga.genre_id = ig.genre_id
  5. AND ig.genre_id = 3
  6.  
  7.  
  8. SELECT COUNT(i.item_id)
  9. FROM items i, item_tipos it, item_tipos_asociados ita
  10. WHERE i.item_id = ita.item_id
  11. AND ita.type_id = it.type_id
  12. AND it.type_id = 3
  13.  
  14.  
  15. SELECT COUNT(i.item_id)
  16. FROM items i, items_categorias ic, items_categorias_asociados icd
  17. WHERE i.item_id = icd.item_id
  18. AND icd.item_category_id = ic.item_category_id
  19. AND ic.item_category_id = 3
  20.  
  21.  
  22. SELECT COUNT(i.item_id)
  23. FROM items i, item_publishers ip, item_publisher_asociados ipa
  24. WHERE i.item_id = ipa.item_id
  25. AND ipa.publisher_id = ip.publisher_id
  26. AND ip.publisher_id = 3
  27.  
  28.  
  29. SELECT COUNT(i.item_id)
  30. FROM items i, item_developers id, item_developer_asoc ida
  31. WHERE i.item_id = ida.item_id
  32. AND ida.developer_id = id.developer_id
  33. AND id.developer_id = 3;

Ahí es cuando se cuelga el ordenador y ya no hace nada.
Ese el es problema, ya que no hay tantos resultados para que se cuelgue la máquina.

Última edición por gnzsoloyo; 13/07/2015 a las 10:57 Razón: SQL sin etiquetar. Usar Highlight "SQL", por favor.
  #4 (permalink)  
Antiguo 13/07/2015, 11:23
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: Query que cuelga el ordenador (para Búsqueda Avanzada)

Cita:
A ver, ea query es solo un ejemplo que recoge campos para ver qué hay en base de datos. Ya sé que no está bien, pero era solo demostrar que una consulta así no puede hacer que se vuelgue el ordenador (que tarde sí, que casque el ordenador no).
En primer lugar, eso no es cierto. Una consulta mal optimizada puede hacer caer en error el servidor de la base de datos, por abuso de recursos, o se abortada sin mensaje de error, con lo que la aplicación no se entera del fallo y se queda "colgada" hasta salir por timeout.
Si las cosas no se optmizan, y no se evalúan los limites del sistema, los fallos no de administran correctamente, y todo parece haberse vuelto loco.

Ahora bien, vayamos al segundo término de tu consulta:Aunque puedan parecer consultas simples en cada uno de los casos, hay tres cosas que debes tener en cuenta cuando trabajas con bases de datos:
1) No todos los DBMS implementaron las mismas cláusulas de la misma forma, por lo que hay diferencia de comportamientos entre ellos. Eso se puede apreciar en el uso de los JOIN implícitos, ya que Oracle, por ejemplo, los asume como INNER JOIN, y espera a ver la definición en la relación del WHERE antes de generar el matching entre registros. Pero no es el caso de MySQL.
MySQL no tiene buena optimización en el WHERE, por lo que en realidad hace el matchig primero y filtrá después. En ese contexto, hace un producto cartesiano y a ese producto le aplica la condición. El problema es que eso genera una tabla de 800.000 registros en memoria swap, que luego filtra (el proceso de filtrado también se hace swapeando a disco). Como es demasiado overhead, el proceso acumulativo de los UNION puede ser calamitoso.
2) MySQL optimiza correctamente el from, por lo que es mucho mejor trabajar con JOIN explícitos y no implícitos (la coma) .Así le indicas que lea sólo lo que necesitas y luego filtre.
Finalmente, el UNION contiene un DISTINCT implícito, por lo que hay un subproceso no visible que realiza cada UNION y luego los discrimina. Eso puede generar muchos errores, y además baja de performance.

¿Como sería una solución aproximada?
Bueno, por lo pronto, elimina los JOIN implícitos, y veamos si mejora la performance, luego, no cuentes cada segmento por el item, sino por lo devuelto. Cuenta todo al final y veamos si mejora:

Código MySQL:
Ver original
  1. SELECT SUM(total) item_id
  2.     SELECT COUNT(1) total
  3.     FROM items i
  4.         INNER JOIN item_generos_asociados iga ON i.item_id = iga.item_id
  5.         INNER JOIN item_generos ig ON iga.genre_id = ig.genre_id
  6.     WHERE ig.genre_id = 3
  7.     UNION ALL
  8.     SELECT COUNT(1) total
  9.     FROM items i
  10.         INNER JOIN item_tipos_asociados ita ON i.item_id = ita.item_id
  11.         INNER JOIN item_tipos it ON ita.type_id = it.type_id
  12.     WHERE it.type_id = 3
  13.     UNION ALL
  14.     SELECT COUNT(1)
  15.     FROM items i
  16.         INNER JOIN items_categorias_asociados icd ON i.item_id = icd.item_id
  17.         INNER JOIN items_categorias ic ON icd.item_category_id = ic.item_category_id
  18.     WHERE
  19.         ic.item_category_id = 3
  20.     UNION ALL
  21.     SELECT COUNT(1) total
  22.     FROM items i
  23.         INNER JOIN item_publisher_asociados ipa ON i.item_id = ipa.item_id
  24.         INNER JOIN item_publishers ip ON ipa.publisher_id = ip.publisher_id
  25.     WHERE
  26.         ip.publisher_id = 3
  27.     UNION ALL
  28.     SELECT COUNT(1) total
  29.     FROM items i
  30.         INNER JOIN item_developer_asoc ida ON i.item_id = ida.item_id
  31.         INNER JOIN item_developers id ON ida.developer_id = id.developer_id
  32.     WHERE
  33.         id.developer_id = 3) tablaTemp;

Probemos así...

Finalmente, y no menos importante: En SQL el orden de los factores si altera el producto. Esto implica que el orden de las tablas debe seguir la lógica de la relación. No se ponen en cualquier posición, o el DBMS te puede generar resultados ineficientes.

En cuanto al EXPLAIN que te comenté antes, es una cláusula de análisis de performance que puedes ver en el manual de referencia y te permite ver anticipadamente cuán performante es la query que usas, y qué modificaciones podrían hacerse.

https://dev.mysql.com/doc/refman/5.6...g-explain.html
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #5 (permalink)  
Antiguo 13/07/2015, 12:15
 
Fecha de Ingreso: julio-2008
Mensajes: 84
Antigüedad: 16 años, 5 meses
Puntos: 0
Respuesta: Query que cuelga el ordenador (para Búsqueda Avanzada)

Al quitar los join implícitos se ha resuelto.
Con el ejemplo que me has puesto, la consulta pasa a tardar unos escasos 300 milisegundos.

El ejemplo funciona perfectamente.
¡¡¡¡¡ Muchísimas gracias !!!!!!!!!!
  #6 (permalink)  
Antiguo 13/07/2015, 12:16
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: Query que cuelga el ordenador (para Búsqueda Avanzada)

__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)

Etiquetas: cuelga, ordenador, query, registro, select, 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 01:13.