Pregunta

Tengo las siguientes tablas:

CREATE TABLE `data` (
  `date_time` decimal(26,6) NOT NULL,
  `channel_id` mediumint(8) unsigned NOT NULL,
  `value` varchar(40) DEFAULT NULL,
  `status` tinyint(3) unsigned DEFAULT NULL,
  `connected` tinyint(1) unsigned NOT NULL,
  PRIMARY KEY (`channel_id`,`date_time`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE `channels` (
  `channel_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `channel_name` varchar(40) NOT NULL,
  PRIMARY KEY (`channel_id`),
  UNIQUE KEY `channel_name` (`channel_name`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

Me preguntaba si alguien podría darme algunos consejos sobre cómo optimizar o reescribir la siguiente consulta:

SELECT channel_name, t0.date_time, t0.value, t0.status, t0.connected, t1.date_time, t1.value, t1.status, t1.connected FROM channels,
    (SELECT MAX(date_time) AS date_time, channel_id, value, status, connected FROM data
        WHERE date_time <= 1300818330
        GROUP BY channel_id) AS t0
    RIGHT JOIN
    (SELECT MAX(date_time) AS date_time, channel_id, value, status, connected FROM data
        WHERE date_time <= 1300818334
        GROUP BY channel_id) AS t1
ON t0.channel_id = t1.channel_id
WHERE channels.channel_id = t1.channel_id

Básicamente, obtengo el valor, el estado y los campos conectados para cada canal_name en dos momentos diferentes. Como T0 es siempre <= T1, los campos podrían existir para T1, pero no T0, y quiero que se muestre eso. Por eso estoy usando la unión correcta. Si no existe para T1, entonces no existirá para T0, por lo que no se debe devolver ninguna fila.

El problema parece ser que desde que me estoy uniendo a las consultas, no se puede usar un índice? Intenté reescribirlo para que se uniera primero en el canal_id de la tabla de datos, pero eso es millones de filas.

También sería bueno poder agregar un campo booleano a cada una de las filas finales que es verdadero cuando t0.value = t1.Value & t0.status = t1.status & t0.connected = t1.connected.

Muchas gracias por tu tiempo.

¿Fue útil?

Solución

Puede reducir las dos subterías a una

SELECT channel_id,
   MAX(date_time) AS t1_date_time,
   MAX(case when date_time <= {$p1} then date_time end) AS t0_date_time
FROM data
WHERE date_time <= {$p2}
GROUP BY channel_id

Grupo de es notoriamente engañoso en MySQL. Imagínese si tuviera min () y max () en la misma selección, ¿de qué fila debe provenir las columnas no agrupadas? Una vez que comprenda esto, verá por qué no es determinista.

Para obtener las filas completas T0 y T1

SELECT x.channel_id,
       t0.date_time, t0.value, t0.status, t0.connected,
       t1.date_time, t1.value, t1.status, t1.connected
FROM (
    SELECT channel_id,
       MAX(date_time) AS t1_date_time,
       MAX(case when date_time <= {$p1} then date_time end) AS t0_date_time
    FROM data
    WHERE date_time <= {$p2}
    GROUP BY channel_id
) x
INNER JOIN data t1 on t1.channel_id = x.channel_id and t1.date_time = x.t1_date_time
LEFT JOIN data t0 on t0.channel_id = x.channel_id and t0.date_time = x.t0_date_time

Y finalmente una unión para obtener el nombre del canal

SELECT c.channel_name,
       t0.date_time, t0.value, t0.status, t0.connected,
       t1.date_time, t1.value, t1.status, t1.connected,
       t0.value=t1.value AND t1.status=t0.status
                         AND t0.connected=t1.connected name_me
FROM (
    SELECT channel_id,
       MAX(date_time) AS t1_date_time,
       MAX(case when date_time <= {$p1} then date_time end) AS t0_date_time
    FROM data
    WHERE date_time <= {$p2}
    GROUP BY channel_id
) x
INNER JOIN channels c on c.channel_id = x.channel_id
INNER JOIN data t1 on t1.channel_id = x.channel_id and t1.date_time = x.t1_date_time
LEFT JOIN data t0 on t0.channel_id = x.channel_id and t0.date_time = x.t0_date_time


EDITAR

Para realizar un nombre de canal de rico en r, parece lo suficientemente simple como para agregar una cláusula Where al final de la consulta en c.channel_name. Sin embargo, puede funcionar mejor filtrarlo en la subconsulta, haciendo uso de la función MySQL de procesar la nota de coma de comisión de izquierda a derecha.

SELECT x.channel_name,
       t0.date_time, t0.value, t0.status, t0.connected,
       t1.date_time, t1.value, t1.status, t1.connected,
       t0.value=t1.value AND t1.status=t0.status
                         AND t0.connected=t1.connected name_me
(
    SELECT c.channel_id, c.channel_name,
       MAX(d.date_time) AS t1_date_time,
       MAX(case when d.date_time <= {$p1} then d.date_time end) AS t0_date_time
    FROM channels c, data d
    WHERE c.channel_name RLIKE {$expr}
      AND c.channel_id = d.channel_id
      AND d.date_time <= {$p2}
    GROUP BY c.channel_id
) x
INNER JOIN data t1 on t1.channel_id = x.channel_id and t1.date_time = x.t1_date_time
LEFT JOIN data t0 on t0.channel_id = x.channel_id and t0.date_time = x.t0_date_time
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top