Question

J'ai les tableaux suivants:

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;

Je me demandais si quelqu'un pouvait me donner des conseils sur la façon d'optimiser ou de réécrire la requête suivante:

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

En gros, j'obtiens la valeur, le statut et les champs connectés pour chaque channel_name à deux moments différents.Puisque t0 est toujours <= t1, les champs peuvent exister pour t1, mais pas pour t0, et je veux que cela soit affiché.C'est pourquoi j'utilise le RIGHT JOIN.S'il n'existe pas pour t1, alors il n'existera pas pour t0, donc aucune ligne ne doit être renvoyée.

Le problème semble être que puisque je rejoins des sous-requêtes, aucun index ne peut être utilisé?J'ai essayé de le réécrire pour faire une auto-jointure sur le channel_id de la table de données en premier, mais c'est des millions de lignes.

Ce serait également bien de pouvoir ajouter un champ booléen à chacune des dernières lignes qui est vrai lorsque t0.value= t1.value & t0.status= t1.status & t0.connected= t1.connected.

Merci beaucoup pour votre temps.

Était-ce utile?

La solution

Vous pouvez réduire les deux sous-requêtes à une seule

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

GROUP BY est notoirement trompeur dans MySQL.Imaginez si vous aviez MIN () et MAX () dans la même sélection, de quelle ligne les colonnes non groupées devraient-elles provenir?Une fois que vous aurez compris cela, vous verrez pourquoi ce n'est pas déterministe.

Pour obtenir les lignes complètes t0 et 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

Et enfin une jointure pour obtenir le nom de la chaîne

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


MODIFIER

Pour effectuer un RLIKE sur le nom du canal, il semble assez simple d'ajouter une clause WHERE à la fin de la requête sur c.channel_name.Il peut cependant être plus efficace de le filtrer au niveau de la sous-requête, en utilisant la fonctionnalité MySQL de traitement des jointures en notation virgule de gauche à droite.

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top