El título del post quizás no sea muy descriptivo, pero el problema es peliagudo: en un blog queremos saber cómo se podría hacer para escoger de los últimos posts aquellos en los que se haya comentado, escoger los 5 últimos, y de cada uno de estos 5 últimos sacar los ids y los nombres de los últimos 3 comentadores. Ahí es nada.
La estructura de las tablas es la siguiente:
| posts |
|---|
| id |
| titulo |
|
comentarios |
|---|
| id |
| post_id |
| autor |
| texto |
Por supuesto esto tiene una solución fácil: haces una selección de los últimos comentarios y escoges los 5 últimos que estén asociados a posts con un distinct en la consulta:
SELECT titulo, id FROM posts p, (SELECT DISTINCT (post_id) FROM comentarios ORDER BY id DESC LIMIT 5) c WHERE p.id = c.post_id
De donde sacas los ids de los últimos posts y sus títulos. Después de esto, con cada uno de los ids puedes sacar los nombres de los últimos comentadores de cada post; introducimos en un bucle esta consulta:
SELECT id, autor FROM comentarios WHERE post_id = "( id del post actual )" ORDER BY id DESC
Con lo que ya tenemos todos los datos, pero hemos necesitado 6 consultas, y para remediarlo... tendremos que utilizar una cosilla que me encanta de MySQL: las variables. Sin olvidarnos de los condicionales.
En principio la solución que había pensado no se parece en casi nada a la final, como suele ocurrir.
Primer intento:
SELECT titulo,@a:=p.id, (SELECT GROUP_CONCAT( CONCAT( c2.id,';',c2.autor)) as nick_concatenado FROM (SELECT id, autor, post_id FROM comentarios WHERE post_id =@a ORDER BY id DESC LIMIT 3) c2 GROUP BY c2.post_id HAVING c2.post_id=@a LIMIT 1) c2 WHERE c.id=c2.id) as nicks FROM posts p, (SELECT DISTINCT (post_id) FROM comentarios ORDER BY id DESC LIMIT 5) c WHERE p.id = c.post_id
La explicación de la query (más bien es la explicación de mi manera de pensar la interpretación de la query): seleccionamos como antes los ids de los posts y lo utilizamos como una tabla, a continuación se une con la tabla de posts por el campo id, metiendo cada id actual de la consulta (el id del post) en la variable @a; hacemos una selección de cada uno de los comentadores limitando la consulta a 3 por post, y a continuación utilizamos la función GROUP_CONCAT(), que devuelve separados por comas todos los registros que se agrupen por un campo. Y con esto lo que pretendía obtener era algo como esto:
| titulo | @a:=p.id | nicks |
|---|---|---|
| Título post 35 | 35 | 256;pepe,250;manolo,240;ana |
| Título post 33 | 33 | 255;pepe,249;manolo,239;ana |
| Título post 30 | 30 | 254;pepe,248;manolo,238;ana |
| Título post 29 | 29 | 253;pepe,247;manolo,237;ana |
| Título post 26 | 26 | 252;pepe,246;manolo,236;ana |
Pero no fue así. El problema está en que la consulta que agrupa los ids y los nombres de los comentaristas se ejecuta antes de que @a tome valor, no porque esté antes en el código, ya que aunque se ejecuta secuencialmente, lo que prima es el grado de anidamiento de la consulta, y esta consulta es mucho más interior que cualquier otra.
A partir de esto podríamos suponer que anidando la consulta llamada 'c', que contiene los últimos 5 post_id, más que la primera estaría solucionado, ¡pues no!
El siguiente problema con el que nos encontramos es que aunque la consulta se ejecute en último lugar, el valor que toma @a es igual al último valor de la consulta 'c', en lugar del valor de esta en cada iteración, pero esto último no sé si es algo lógico, o es que después de un largo rato con esto lie tanto la consulta que me quedé atrapado ahí. Fuera como fuera, el resultado es que decidí hacerlo de otra manera.
Segundo método
SET @cont:=-1, @post:=0; SELECT p.titulo, w.nicks, p.id FROM posts p, ( SELECT post_id, GROUP_CONCAT(CONCAT(id,';',autor)) as nicks FROM ( SELECT IF(@post=c.post_id,@cont:=@cont+1,@cont:=0) as contador, @post:=c.post_id, c.post_id, c.id, c.autor FROM comentarios c, (SELECT DISTINCT (post_id) FROM comentarios ORDER BY id DESC LIMIT 5) j WHERE c.post_id=j.post_id) k WHERE contador<3 GROUP BY post_id) w WHERE p.id=w.post_id
Este sí que funciona. Se basa en lo mismo que el anterior, solo que variando quién controla los LIMIT: antes dejaba que MySQL se encargara de ello, ahora me hago mi propio método de LIMIT con variables e IFs. Selecciono como antes los post_id, lo uno a la tabla comentarios para que me devuelva la tabla con un campo nuevo al que he llamado contador (que es incremental), que para cada post diferente se reinicia a 0; en la siguiente subconsulta escojo solo aquellos cuyo contador sea menor que 3 y con eso ya solo tengo que hacer el GROUP_CONCAT() y unir con la tabla de posts. Fin de la historia.
Perdón si es un poco lioso, pero me suelo explicar bastante mal.