MySQL múltiples uniones izquierdas que causan valores de conteo incorrectos

Así que estoy tratando de escribir una sola consulta de MySQL para reemplazar lo que se está haciendo actualmente en PHP con tres consultas separadas. Tengo una tabla para Categorías con solo dos valores: una ID y un Nombre. Hay otras dos tablas, cada una de las cuales tiene una clave externa que se refiere a la ID de la primera tabla. Necesito obtener una lista de todos los valores de la tabla Categorías, así como un recuento de cuántas veces se hace referencia a cada valor en cada una de las otras dos tablas. Puedo obtener el recuento con precisión de cada una de las segundas tablas usando una consulta separada para cada una:

SELECT nga_calevir_event_categories.*, COUNT(nga_calevir_events.event_id) AS event_total
FROM nga_calevir_event_categories
LEFT JOIN (nga_calevir_events)
ON nga_calevir_event_categories.id = nga_calevir_events.event_category
GROUP BY nga_calevir_event_categories.id;

y

SELECT nga_calevir_event_categories.*, COUNT(nga_usermeta.user_id) AS member_total
FROM nga_calevir_event_categories
LEFT JOIN (nga_usermeta)
ON (nga_usermeta.meta_value = nga_calevir_event_categories.id
AND nga_usermeta.meta_key = 'category_id')
GROUP BY nga_calevir_event_categories.id;

Sin embargo, cuando trato de combinar estas consultas, comienza a dar resultados incorrectos para ambos recuentos. Aquí está la consulta combinada:

SELECT nga_calevir_event_categories.*,
COUNT(nga_calevir_events.event_id) AS event_total,
COUNT(nga_usermeta.user_id) AS member_total
FROM nga_calevir_event_categories
LEFT JOIN (nga_calevir_events)
ON nga_calevir_event_categories.id = nga_calevir_events.event_category
LEFT JOIN (nga_usermeta)
ON (nga_usermeta.meta_value = nga_calevir_event_categories.id
AND nga_usermeta.meta_key = 'category_id')
GROUP BY nga_calevir_event_categories.id;

Parece estar duplicando el conteo para la primera fila. He estado experimentando con esto durante un par de horas y simplemente no puedo resolverlo. ¿Algunas ideas? Avíseme si necesita más información para ayudarme con esto.

preguntado el 12 de junio de 12 a las 20:06

1 Respuestas

Esto no funciona porque las uniones son combinatorias:

SELECT nga_calevir_event_categories.*,
COUNT(nga_calevir_events.event_id) AS event_total,
COUNT(nga_usermeta.user_id) AS member_total


FROM nga_calevir_event_categories
LEFT JOIN (nga_calevir_events)
ON nga_calevir_event_categories.id = nga_calevir_events.event_category
-- The above is almost the same as an inner join, so long as every 
-- category has at least one event.

LEFT JOIN (nga_usermeta)
ON (nga_usermeta.meta_value = nga_calevir_event_categories.id
AND nga_usermeta.meta_key = 'category_id')
-- This is where you're going to get weird.  This is going to join 
-- every row already figured out- basically, a row for every event
-- with every usermeta that matches!


GROUP BY nga_calevir_event_categories.id;

Esto realmente son dos consultas: no hay una forma sensata de unir usermeta a categorías (a menos que no entienda bien la aplicación). Sin embargo, hay una manera de incluirlo en una declaración SQL. Prueba esto:

SELECT categories.*,
       coalesce(event_hits.hits, 0) + coalesce(meta_hits.hits, 0) as total_hits
FROM nga_calevir_event_categories as categories       

LEFT JOIN
(select event_category as catid, count(*) as hits
    FROM (nga_calevir_events)
    group by event_category
) as event_hits ON event_hits.catid = categories.id

LEFT JOIN (
   select meta_value as catid, count(*) as hits
   FROM nga_usermeta
   WHERE meta_key = 'category_id'
   group by meta_value
) as meta_hits ON meta_hits.catid = categories.id

Esto utiliza dos selecciones internas para obtener un recuento de los ID de categoría utilizados para cada una de las dos tablas, luego une esas selecciones internas a las categorías reales y suma los recuentos que cada una devolvió. Usamos COALESCE para asegurarnos de que donde no hubo usos en una de las tablas, se asume 0.

¡Espero que esto ayude!

Respondido el 14 de junio de 12 a las 23:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.