2009-03-13 8 views
1

Il doit exister une meilleure façon d'écrire cette requête.Développer la requête au-delà de celle spécifiée dans la clause WHERE

Je veux sélectionner toutes les données entre une paire de dates. Idéalement, les première et dernière lignes du jeu de résultats seraient celles spécifiées dans la clause WHERE. Si ces lignes n'existent pas, je veux que les lignes précèdent et suivent la plage demandée.

Un exemple:

Si mes données sont:

... 
135321, 20090311 10:15:00 
135321, 20090311 10:45:00 
135321, 20090311 11:00:00 
135321, 20090311 11:15:00 
135321, 20090311 11:30:00 
135321, 20090311 12:30:00 
... 

Et la requête est:

SELECT * 
    FROM data_bahf 
    WHERE param_id = 135321 
    AND datetime >= '20090311 10:30:00' 
    AND datetime <= '20090311 12:00:00' 

Je veux que les données renvoyées pour inclure la ligne à 10h15, et celui de 12h30. Pas seulement ceux qui respectent strictement la clause WHERE.

C'est le meilleur que j'ai trouvé.

SELECT * FROM (
    SELECT * 
    FROM data_bahf 
    WHERE param_id = 135321 
    AND datetime > '20090311 10:30:00' 
    AND datetime < '20090311 12:00:00' 

    UNION 

    (
     SELECT * FROM data_bahf 
     WHERE param_id = 135321 
     AND datetime <= '20090311 10:30:00' 
     ORDER BY datetime desc 
     LIMIT 1 
    ) 

    UNION 

    (
     SELECT * FROM data_bahf 
     WHERE param_id = 135321 
     AND datetime >= '20090311 12:00:00' 
     ORDER BY datetime asc 
     LIMIT 1 
    ) 
) 
AS A 
ORDER BY datetime 

(Ignorer l'utilisation de SELECT * pour l'instant)

EDIT: Je index sur les param_id, datetime, et (param_id, datetime)

+0

Vous devrez ajouter param_id dans vos clauses ORDER BY pour l'index sur (param_id, datetime) à utiliser, comme ceci: ORDER BY param_id, datetime; ORDER BY param_id DESC, datetime DESC – Quassnoi

Répondre

2

Tout d'abord, assurez-vous que vous avez un indice composite sur (param_id, datetime)

En second lieu, requête comme ceci:

SELECT * 
FROM data_bahf 
WHERE param_id = 135321 
     AND datetime BETWEEN 
     COALESCE(
     (
     SELECT MAX(datetime) 
     FROM data_bahf 
     WHERE param_id = 135321 
       AND datetime <= '2009-01-01 00:00:00' 
     ), '0001-01-01') 
     AND 
     COALESCE(
     (
     SELECT MIN(datetime) 
     FROM data_bahf 
     WHERE param_id = 135321 
       AND datetime >= '2009-01-02 00:00:00' 
     ), '9999-01-01') 

Juste vérifié, il fonctionne en 1.215 ms pour une table échantillon de 200,000 lignes

+0

Pouvez-vous dire pourquoi avoir un indice composite est bénéfique? (pas de critique, je me demande juste) – Tomalak

+0

Parce que si vous n'en avez pas, vous devrez filtrer tous les param_id's non-correspondants en recherchant MAX et MIN. – Quassnoi

+0

Quand vous avez déjà un index sur "param_id" (que j'ai pris comme lu pour cette question) je m'attendrais à ce que l'optimiseur l'applique avant qu'il ne fasse MAX() ou MIN() sur les lignes restantes. Je pense donc avoir deux index séparés pour donner la même performance. (Ou pas?) – Tomalak

3

Je dirais que cela:

SELECT 
    o.* 
FROM 
    data_bahf o 
WHERE 
    o.param_id = 135321 
    AND o.datetime BETWEEN 
    ISNULL(
    (
     SELECT MAX(datetime) 
     FROM  data_bahf i 
     WHERE i.param_id = 135321 AND i.datetime <= '20090311 10:30:00' 
    ), 
    '0001-01-01 00:00:00' 
) 
    AND 
    ISNULL(
    (
     SELECT MIN(datetime) 
     FROM  data_bahf i 
     WHERE i.param_id = 135321 AND i.datetime >= '20090311 12:00:00' 
    ), 
    '9999-12-31 23:59:59' 
) 

EDIT: repli ajouté.
S'il n'y a pas de ligne correspondant à la sous-requête, la valeur NULL doit être atteinte, qui doit être capturée par ISNULL() ou l'opérateur BETWEEN échouera et la requête principale ne retournera aucune ligne du tout.

+0

Ça a l'air mieux - mais c'est 10 fois plus lent! – Johan

+0

Avez-vous un index sur votre champ datetime? (Je prends pour acquis que vous en avez un sur votre champ param_id.) – Tomalak

+0

Je le fais, et la requête l'utilise. Les temps sont de 10 ms pour la requête originale et de 200 ms pour la nouvelle. En termes absolus, ce n'est pas beaucoup, mais j'ai beaucoup de ces requêtes à exécuter. Il n'y a qu'environ 200k lignes. – Johan