Domanda

Ho le seguenti tabelle:

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;

Mi chiedevo se qualcuno potesse darmi qualche consiglio su come ottimizzare o riscrivere la seguente domanda:

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

Fondamentalmente ottengo il valore, lo stato e i campi connessi per ciascun canale_name in due volte diversi. Poiché T0 è sempre <= T1, i campi potrebbero esistere per T1, ma non T0, e voglio che sia mostrato. Ecco perché sto usando il giusto join. Se non esiste per T1, allora non esiste per T0, quindi nessuna riga dovrebbe essere restituita.

Il problema sembra essere che dal momento che mi unisco alle domande secondarie, non è possibile utilizzare alcun indice? Ho provato a riscriverlo per fare prima un join su canale della tabella dei dati, ma sono milioni di righe.

Sarebbe anche bello poter aggiungere un campo booleano a ciascuna delle righe finali che è vera quando T0.Value = T1.Value & T0.Status = T1.Status & T0.Connected = T1.Connected.

La ringrazio molto per il vostro tempo.

È stato utile?

Soluzione

È possibile ridurre le due sotto-domande 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

Il gruppo di è notoriamente fuorviante in Mysql. Immagina se hai avuto min () e max () nella stessa selezione, da quale riga dovrebbero provenire le colonne non gruppi? Una volta capito, vedrai perché non è deterministico.

Per ottenere le file T0 e T1 complete

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

E infine un join per ottenere il nome del canale

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


MODIFICARE

Per eseguire una rlike sul nome del canale, sembra abbastanza semplice da aggiungere una clausola dove alla fine della query c.channel_name. Può tuttavia funzionare meglio filtrarlo nella sottoquery, utilizzando la funzionalità MySQL dell'elaborazione di una notazione di virgola da sinistra a destra.

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top