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

Evitar producto cartesiano entre tablas

Estas en el tema de Evitar producto cartesiano entre tablas en el foro de Mysql en Foros del Web. Hola amigos. Tengo una tabla maestra llamada planificacion y dos tablas detalles (objetivos y acciones). La clave foránea es codplanificacion. La tabla objetivos tiene 3 ...
  #1 (permalink)  
Antiguo 06/11/2013, 11:47
 
Fecha de Ingreso: junio-2013
Ubicación: Argentina
Mensajes: 24
Antigüedad: 10 años, 10 meses
Puntos: 0
Evitar producto cartesiano entre tablas

Hola amigos.
Tengo una tabla maestra llamada planificacion y dos tablas detalles (objetivos y acciones). La clave foránea es codplanificacion. La tabla objetivos tiene 3 registros y la tabla acciones 2. Si hago
Código SQL:
Ver original
  1. SELECT *
  2. FROM planificacion
  3. INNER JOIN objetivos ON objetivos.codplanificacion = planificacion.codplanificacion
  4. INNER JOIN acciones ON acciones.codplanificacion = planificacion.codplanificacion

me devuelve 6 registros, debido al producto cartesiano entre las tablas. Lo que que quiero hacer es que la cantidad de registros devueltos sea de la tabla que tenga más registros, ya sea objetivos o acciones.

Saludos.
  #2 (permalink)  
Antiguo 06/11/2013, 12: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: 16 años, 4 meses
Puntos: 2658
Respuesta: Evitar producto cartesiano entre tablas

Cita:
me devuelve 6 registros, debido al producto cartesiano entre las tablas
Eso no es un producto cartesiano, sino la consecuencia de una relación N:N entre las tres tablas.
No confundas.
Producto cartesiano sería si cada registro de una tabla hace un JOIn con cada uno de la otra, en cadena, incluyendo aquellos registros donde no existe una relacion declarada de FK.
En tu caso, si devuelve 6 registros debe estar reptiendo los datos propios de las dos primeras tablas, pero los de la tercera deben ser distintos en algunos valores, ¿no?
Supongamos que te está devolviendo uno solo relacionado con un registro de la primera tabla con uno de la segunda y uno de la tercera. Luego uno de la primera con uno de la segunda, y con cinco de la tercera.

Eso no es producto cartesiano (PC). Si fuese un PC, en ese ejemplo debería devolverte venticuatro registros, no seis...
Es decir, dos de de la primera con cada uno de los dos de la segunda, y cada uno de esos con seis de la tercera... Serían 2 x 2 x 6.

¿Se entiende?
__________________
¿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 06/11/2013, 13:54
 
Fecha de Ingreso: junio-2013
Ubicación: Argentina
Mensajes: 24
Antigüedad: 10 años, 10 meses
Puntos: 0
Respuesta: Evitar producto cartesiano entre tablas

Muchas gracias Gonzalo por contestar y por refrescarme algunos conceptos teóricos, que estaban cubiertos de polvo, jeje.

Cita:
En tu caso, si devuelve 6 registros debe estar reptiendo los datos propios de las dos primeras tablas, pero los de la tercera deben ser distintos en algunos valores, ¿no?
¿Tendía que crear una subsconsulta para eliminar los datos duplicados?
  #4 (permalink)  
Antiguo 06/11/2013, 15:55
Colaborador
 
Fecha de Ingreso: enero-2007
Ubicación: México
Mensajes: 2.097
Antigüedad: 17 años, 3 meses
Puntos: 447
Respuesta: Evitar producto cartesiano entre tablas

Hola beto13:

Mucho Ojo... tal como comenta gnzsoloyo, el problema es que entre tus tablas hay una relación m a n, por lo tanto puede dar la impresión de que existe información duplicada, pero eso no es asi... no nos dices cómo es la estructura de tus tablas, ni nos pones datos de ejemplos, pero creo que tienes algo como esto:

Código MySQL:
Ver original
  1. mysql> SELECT * FROM planificacion;
  2. +-----------------+-------------------+
  3. | idPlanificacion | descripcion       |
  4. +-----------------+-------------------+
  5. |               1 | planificacion uno |
  6. |               2 | planificacion dos |
  7. +-----------------+-------------------+
  8. 2 rows in set (0.00 sec)
  9.  
  10. mysql> SELECT * FROM objetivos;
  11. +------------+-----------------+--------------+
  12. | idObjetivo | idPlanificacion | descripcion  |
  13. +------------+-----------------+--------------+
  14. |          1 |               1 | objetivo 1.1 |
  15. |          2 |               2 | objetivo 2.1 |
  16. |          3 |               2 | objetivo 2.2 |
  17. +------------+-----------------+--------------+
  18. 3 rows in set (0.00 sec)
  19.  
  20. mysql> SELECT * FROM acciones;
  21. +----------+-----------------+-------------+
  22. | idAccion | idPlanificacion | descripcion |
  23. +----------+-----------------+-------------+
  24. |        1 |               1 | accion 1.1  |
  25. |        2 |               2 | accion 2.1  |
  26. |        3 |               2 | accion 2.2  |
  27. +----------+-----------------+-------------+
  28. 3 rows in set (0.00 sec)

es decir, dos planificaciones, la primera con 1 objetivo y una accion y la segunda con dos objetivos y dos acciones... si haces una consulta como la que planeas obtienes esto:

Código MySQL:
Ver original
  1. mysql> SELECT *
  2.     -> FROM planificacion
  3.     -> INNER JOIN objetivos
  4.     ->   ON objetivos.idPlanificacion = planificacion.idPlanificacion
  5.     -> INNER JOIN acciones
  6.     ->   ON acciones.idPlanificacion = planificacion.idPlanificacion;
  7. +-----------------+-------------------+------------+-----------------+--------------+----------+-----------------+-------------+
  8. | idPlanificacion | descripcion       | idObjetivo | idPlanificacion | descripcion  | idAccion | idPlanificacion | descripcion |
  9. +-----------------+-------------------+------------+-----------------+--------------+----------+-----------------+-------------+
  10. |               1 | planificacion uno |          1 |               1 | objetivo1.1 |        1 |               1 | accion 1.1  |
  11. |               2 | planificacion dos |          2 |               2 | objetivo2.1 |        2 |               2 | accion 2.1  |
  12. |               2 | planificacion dos |          3 |               2 | objetivo2.2 |        2 |               2 | accion 2.1  |
  13. |               2 | planificacion dos |          2 |               2 | objetivo2.1 |        3 |               2 | accion 2.2  |
  14. |               2 | planificacion dos |          3 |               2 | objetivo2.2 |        3 |               2 | accion 2.2  |
  15. +-----------------+-------------------+------------+-----------------+--------------+----------+-----------------+-------------+
  16. 5 rows in set (0.00 sec)

entonces podrías pensar que hay registros duplicados, porque aparecen cuatro registros para la planificación dos...

Esto sin embargo no es correcto, el problema insisto es que tienes una relación M a N. No nos dices en realidad qué esperas obtener como salida, pero aquí va una posible solución... Puedes "consolidar" tus tablas, de tal manera que en lugar de tener una relación 1 a N entre planificaciones-objetivos y planificaciones-acciones, cambie a una relación 1 a 1, por ejemplo con la función GROUP-CONCAT...

Código MySQL:
Ver original
  1. mysql> SELECT idPlanificacion, GROUP_CONCAT(descripcion) descripcion
  2.     -> FROM objetivos
  3.     -> GROUP BY idPlanificacion;
  4. +-----------------+---------------------------+
  5. | idPlanificacion | descripcion               |
  6. +-----------------+---------------------------+
  7. |               1 | objetivo 1.1              |
  8. |               2 | objetivo 2.1,objetivo 2.2 |
  9. +-----------------+---------------------------+
  10. 2 rows in set (0.00 sec)

Observa que sólo se muestra un registro para el idPlanifiación2, pero que en el campo descripción se muestran las dos descripciones separadas por comas... de esta manera podrías hacer algo como esto:

Código MySQL:
Ver original
  1. mysql> SELECT P.*, O.descripcion objetivos, A.descripcion acciones
  2.     -> FROM planificacion P
  3.     -> INNER JOIN
  4.     ->    ( SELECT idPlanificacion, GROUP_CONCAT(descripcion) descripcion
  5.     ->      FROM objetivos
  6.     ->      GROUP BY idPlanificacion ) O
  7.     ->   ON O.idPlanificacion = P.idPlanificacion
  8.     -> INNER JOIN
  9.     ->    ( SELECT idPlanificacion, GROUP_CONCAT(descripcion) descripcion
  10.     ->      FROM acciones
  11.     ->      GROUP BY idPlanificacion ) A
  12.     ->   ON A.idPlanificacion = P.idPlanificacion;
  13. +-----------------+-------------------+---------------------------+-----------------------+
  14. | idPlanificacion | descripcion       | objetivos                 | acciones              |
  15. +-----------------+-------------------+---------------------------+-----------------------+
  16. |               1 | planificacion uno | objetivo 1.1              | accion 1.1            |
  17. |               2 | planificacion dos | objetivo 2.1,objetivo 2.2 | accion 2.1,accion 2.2 |
  18. +-----------------+-------------------+---------------------------+-----------------------+
  19. 2 rows in set (0.01 sec)

De tal manera que ya no hay información repetida...

podrías cambiar los INNER JOIN's por LEFT JOIN's, por si hubiera el caso de planificaciones sin objetivos o sin acciones.

No sé si esto te pueda servir, si continuas con problemas postea algunos datos de ejemplo de tus tablas y dinos qué es lo que esperas como salida.

Saludos
Leo.
  #5 (permalink)  
Antiguo 07/11/2013, 06:55
 
Fecha de Ingreso: junio-2013
Ubicación: Argentina
Mensajes: 24
Antigüedad: 10 años, 10 meses
Puntos: 0
De acuerdo Respuesta: Evitar producto cartesiano entre tablas

leonardo_josue te pasaste con tu explicación. La relación entre las tablas es 1:N




Tendré que modificar mi query para que mi objeto json no sea muy grande.

Muchas gracias.

Última edición por beto13; 07/11/2013 a las 06:56 Razón: No salían las caritas

Etiquetas: join, producto, registro, select, tabla, tablas
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 05:39.