2011-06-07 18 views
12

Tengo una consulta que obtiene toda la información que necesito para la página principal de un sistema de mensajería (incluyendo recuento de mensajes no leídos, etc.) ... pero actualmente recupera el mensaje original. Me gustaría aumentar la consulta a continuación para obtener el mensaje más reciente en cada hilo.Obteniendo el mensaje más reciente en un hilo

Esta consulta está muy cerca, sin embargo mis conocimientos de SQL mediocres me están guardando de envolver las cosas ...

$messages = array(); 
$unread_messages_total = 0; 

$messages_query = " 
SELECT m.* 
    , COUNT(r.id) AS num_replies 
    , MAX(r.datetime) AS reply_datetime 
    , (m.archived NOT LIKE '%,".$cms_user['id'].",%') AS message_archive 
    , (m.viewed LIKE '%,".$cms_user['id'].",%') AS message_viewed 
    , SUM(r.viewed NOT LIKE '%,".$cms_user['id'].",%') AS unread_replies 
    , CASE 
     WHEN MAX(r.datetime) >= m.datetime THEN MAX(r.datetime) 
     ELSE m.datetime 
     END AS last_datetime 
FROM directus_messages AS m 
LEFT JOIN directus_messages as r ON m.id = r.reply 
WHERE m.active = '1' 
AND (m.to LIKE '%,".$cms_user['id'].",%' OR m.to = 'all' OR m.from = '".$cms_user['id']."') 
GROUP BY m.id 
HAVING m.reply = '0' 
ORDER BY last_datetime DESC"; 

foreach($dbh->query($messages_query) as $row_messages){ 
    $messages[] = $row_messages; 
    $unread_messages_total += (strpos($row_messages['archived'], ','.$cms_user['id'].',') === false && ((strpos($row_messages['viewed'], ','.$cms_user['id'].',') === false && $row_messages['unread_replies'] == NULL) || ($row_messages['unread_replies']>0 && $row_messages['unread_replies'] != NULL)))? 1 : 0; 
} 

Gracias de antemano por cualquier ayuda que puede proporcionar!

EDIT: (base de datos)

CREATE TABLE `cms_messages` (
    `id` int(10) NOT NULL auto_increment, 
    `active` tinyint(1) NOT NULL default '1', 
    `subject` varchar(255) NOT NULL default '', 
    `message` text NOT NULL, 
    `datetime` datetime NOT NULL default '0000-00-00 00:00:00', 
    `reply` int(10) NOT NULL default '0', 
    `from` int(10) NOT NULL default '0', 
    `to` varchar(255) NOT NULL default '', 
    `viewed` varchar(255) NOT NULL default ',', 
    `archived` varchar(255) NOT NULL default ',', 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; 

EDIT 2: (Requisitos)

  • Volver todos los mensajes de los padres para una específicauser_id: $cms_user['id']
  • devolverá el número de respuestas que mensaje primario: num_replies
  • Devuelve el número de leídos respuestas para que el mensaje de los padres: unread_replies
  • devolver la fecha del mensaje padre o su respuesta más reciente: last_datetime
  • retorno si el mensaje está en el archivo: message_archive
  • retorno si el mensaje ha sido visto : message_viewed
  • Volver todos los mensajes en orden de fecha y hora DESC
  • Volver message la más reciente, de los padres o respuestas si hay alguna (como gmail)
+0

¿Puede adjuntar el esquema de la tabla? y las lógicas de cómo identificar el mensaje reciente. thanx – yoavmatchulsky

+0

@yoavmatchulsky - Esquema añadido. Las filas son mensajes principales ('reply = 0') o respuestas (' reply = parent_id'). Me gustaría tener el mensaje principal si no hay respuestas, o la respuesta más reciente de lo contrario (última respuesta para ese ID de mensaje padre ordenado por fecha y hora DESC). ¿Tiene sentido? – RANGER

+0

¿Tiene mensajes de anidación profundos? o solo un nivel? – yoavmatchulsky

Respuesta

5

Si sólo tiene 2 niveles de mensajes (es decir, sólo los mensajes de padres y respuestas directas), puede probar esta consulta:

select 
    root_message.id, 
    root_message.active, 
    root_message.subject, 
    case 
     when max_reply_id.max_id is null then 
      root_message.message 
     else 
      reply_message.message 
    end as message, 
    root_message.datetime, 
    root_message.reply, 
    root_message.from, 
    root_message.to, 
    root_message.viewed, 
    root_message.archived 
from 
    -- basic data 
    cms_messages as root_message 
    -- ID of last reply for every root message 
    left join (
     select 
      max(id) as max_id, 
      reply as parent_id 
     from 
      cms_messages 
     where 
      reply <> 0 
     group by 
      reply 
    ) as max_reply_id on max_reply_id.parent_id = root_message.id            
    left join cms_messages as reply_message on reply_message.id = max_reply_id.max_id 
where 
    root_message.reply = 0 

Utiliza subconsulta max_reply_id como fuente de datos para seleccionar ID de la última responder. Si existe (es decir, si hay respuestas), se usa reply_message.message. Si no existe (no se ha encontrado una respuesta para el mensaje raíz), se usa root_message.message.

También debería pensar acerca de la estructura de la tabla. Por ejemplo, tendría más sentido si reply contiene NULL, si es un mensaje principal o ID de un mensaje existente. Actualmente, lo configura en 0 (ID del mensaje inexistente), lo cual es incorrecto.Los tipos de viewed y archived también son raros.

Editar: también debe evitar el uso de having cláusula. Use where en su lugar, cuando sea posible.


Aquí hay una nueva consulta que debe cumplir con sus requisitos. Si hay algún problema con esto (es decir, si devuelve datos incorrectos), avíseme.

Al igual que la primera consulta, que:

  • utiliza subconsulta reply_summary para acumular datos sobre las respuestas (ID de la última respuesta, el número de respuestas y número de respuestas sin leer);
  • se une a esta sub consulta a la tabla base;
  • se une a cms_messages as reply_message a la subconsulta, según reply_summary.max_reply_id, para obtener datos sobre la última respuesta (mensaje, fecha y hora).

He simplificado la manera cómo se determina last_datetime - que ahora tiene ya sea hora de la última respuesta (si hay alguna respuesta), o el tiempo de post original (cuando no se encuentran respuestas).

No he filtrado las respuestas por from y to campos. Si es necesario, la subconsulta where de reply_summary debe actualizarse.

select 
    parent_message.id, 
    parent_message.subject, 
    parent_message.message, 
    parent_message.from, 
    parent_message.to, 
    coalesce(reply_summary.num_replies, 0) as num_replies, 
    last_reply_message.datetime as reply_datetime, 
    (parent_message.archived NOT LIKE '%,{$cms_user['id']},%') AS message_archive, 
    (parent_message.viewed LIKE  '%,{$cms_user['id']},%') AS message_viewed, 
    reply_summary.unread_replies, 
    coalesce(last_reply_message.message, parent_message.message) as last_message, 
    coalesce(last_reply_message.datetime, parent_message.datetime) as last_datetime 
from 
    cms_messages as parent_message 
    left join (
     select 
      reply as parent_id, 
      max(id) as last_reply_id, 
      count(*) as num_replies, 
      sum(viewed not like '%,{$cms_user['id']},%') as unread_replies 
     from 
      cms_messages 
     where 
      reply <> 0 and 
      active = 1 
     group by 
      reply 
    ) as reply_summary on reply_summary.parent_id = parent_message.id 
    left join cms_messages as last_reply_message on last_reply_message.id = reply_summary.last_reply_id 
where 
    parent_message.reply = 0 and 
    parent_message.active = 1 and 
    (parent_message.to like '%,{$cms_user['id']},%' or parent_message.to = 'all' or parent_message.from = '{$cms_user['id']}') 
order by 
    last_datetime desc; 
+0

Puedo cambiar la respuesta para permitir 'NULL' en lugar de' 0', pero su consulta no parece recibir mensajes específicos para un 'user_id' como lo hace el original. Tampoco calcula 'num_replies' para cada artículo principal ... He agregado los requisitos iniciales a la publicación. – RANGER

+0

Su pregunta inicial fue sobre obtener el "mensaje más reciente" en lugar del mensaje original. Te di solución perfectamente funcional (?) Para eso. Para filtrar los mensajes de un usuario específico, simplemente actualice la cláusula 'where'. Para obtener el número de respuestas, puede incluir 'select (*) como number_of_replies' en la subconsulta' max_reply_id' (y en la lista de valores que se seleccionará en la consulta principal, por supuesto). – binaryLV

+1

@cbh, si necesita ayuda, solicítela. Si necesita una solución 100% completa, pague por ello. No tenemos mucho tiempo para brindarle soluciones al 100%, aunque podemos dar pistas y podemos ayudar a resolver los problemas, pero también debe participar en lugar de solo dar los requisitos. – binaryLV

2

Me temo que no podrá resolver este problema con una sola consulta. O tiene que usar más consultas y recopilar la información en el código circundante o tendrá que rediseñar la estructura de la base de datos para su sistema de mensajería un poco (tablas: hilos, publicaciones, etc.). Si decide rediseñar la estructura de la base de datos, también debe ocuparse de la forma en que maneja los campos viewed y archived. La forma en que usa los campos (¡varchar 255 solamente!) Podría funcionar para algunos usuarios, pero tan pronto como haya más usuarios e ID de usuario más altos, su sistema de mensajes se descompondrá.

+0

Gracias por la respuesta. Esto es para un sistema cerrado con un número limitado de usuarios, por lo que el límite de 255 no será un problema. – RANGER

3

Su problema es que está obteniendo solo m registros independientemente del orden de los registros de r.

intento de añadir

SELECT m.*, r.* 

o

SELECT r.*, m.* 

si está utilizando PDO :: FETCH_ASSOC como el modo de DOP fetch (suponiendo que está utilizando PDO para acceder a su base de datos), el resultado será una matriz asociativa en la que si el conjunto de resultados contiene varias columnas con el mismo nombre, PDO :: FETCH_ASSOC devuelve solo un valor único por nombre de columna. no estoy seguro de qué orden toma presidencia, por lo que tendría que probar ambos.

si sus columnas están definidas en el orden correcto, devolverán el valor r. *, Si existe, o el valor m *. Si no existen registros. ¿Esto tiene sentido? de esta manera, su conjunto de resultados contendrá el último registro sin importar qué tabla (mo) los contenga.

http://www.php.net/manual/en/pdo.constants.php

Cuestiones relacionadas