2008-11-21 5 views
3

J'ai une table contenant les temps d'exécution pour les générateurs sur différents sites, et je veux sélectionner l'entrée la plus récente pour chaque site. Chaque générateur est exécuté une ou deux fois par semaine.Les requêtes en ligne sont-elles une mauvaise idée?

J'ai une requête qui fera cela, mais je me demande si c'est la meilleure option. Je ne peux pas m'empêcher de penser que l'utilisation de WHERE x IN (SELECT ...) est paresseuse et n'est pas la meilleure façon de formuler la requête - n'importe quelle requête.

Le tableau se présente comme suit:

CREATE TABLE generator_logs (
    id integer NOT NULL, 
    site_id character varying(4) NOT NULL, 
    start timestamp without time zone NOT NULL, 
    "end" timestamp without time zone NOT NULL, 
    duration integer NOT NULL 
); 

Et la requête:

SELECT id, site_id, start, "end", duration 
FROM generator_logs 
WHERE start IN (SELECT MAX(start) AS start 
       FROM generator_logs 
       GROUP BY site_id) 
ORDER BY start DESC 

Il n'y a pas une énorme quantité de données, donc je ne suis pas inquiet pour l'optimisation de la requête. Cependant, je dois faire des choses similaires sur des tables avec des dizaines de millions de lignes, (de grandes tables en ce qui me concerne!) Et l'optimisation est plus importante.

Y a-t-il une meilleure requête pour cela, et les requêtes en ligne sont-elles généralement une mauvaise idée?

+0

vous signifie probablement * sous_requête *, pas une requête "inline"? – TMS

Répondre

1

j'utiliser rejoint comme ils font beaucoup mieux alors "IN" clause:

select gl.id, gl.site_id, gl.start, gl."end", gl.duration 
from 
    generator_logs gl 
    inner join (
     select max(start) as start, site_id 
     from generator_logs 
     group by site_id 
    ) gl2 
     on gl.site_id = gl2.site_id 
     and gl.start = gl2.start 

également que Tony pointed out vous manque corrélation dans votre requête d'origine

0

En MYSQL il pourrait être problématique parce que la dernière je l'ai vérifié n'a pas pu optimiser les sous-requêtes efficacement (Ie: par la réécriture de la requête)

De nombreux SGBD ont les planificateurs génétiques des requêtes qui fera la même chose indépendamment de vos requêtes d'entrée structure. MYSQL créera dans certains cas une table temporaire, d'autres fois non, et selon les circonstances, l'indexation, les conditions, les sous-requêtes peuvent être assez rapides.

Certains se plaignent que les sous-requêtes sont difficiles à lire, mais ils sont parfaitement bien si vous les fork dans des variables locales.

$maxids = 'SELECT MAX(start) AS start FROM generator_logs GROUP BY site_id'; 
$q ="  
    SELECT id, site_id, start, \"end\", duration 
     FROM generator_logs 
     WHERE start IN ($maxids) 
     ORDER BY start DESC 
"; 
+0

Qui a dit quelque chose à propos de MySQL? –

+0

C'était plus un commentaire qu'une grande partie de l'opposition que vous voyez dans TRW est basée sur mysql étant mauvais à cela. Et une fois que vous apprenez à penser en ligne, il est difficile de ne pas y penser, et vous finirez par être obligé d'utiliser mysql un jour et vous penserez toujours que les requêtes en ligne sont bonnes et vous vous demanderez pourquoi les performances sont si abyssales . –

4

Votre requête ne doit-elle pas être corrélée? i.e. .:

SELECT id, site_id, start, "end", duration 
FROM generator_logs g1 
WHERE start = (SELECT MAX(g2.start) AS start 
       FROM generator_logs g2 
       WHERE g2.site_id = g1.site_id) 
ORDER BY start DESC 

Sinon, vous potentiellement ramasser non-derniers journaux dont la valeur début passe pour correspondre au dernier départ pour un autre site.

Ou bien:

SELECT id, site_id, start, "end", duration 
FROM generator_logs g1 
WHERE (site_id, start) IN (SELECT site_id, MAX(g2.start) AS start 
          FROM generator_logs g2 
          GROUP BY site_id) 
ORDER BY start DESC 
0

Ce problème - trouver non seulement le MAX, mais le reste de la ligne correspondante - est un problème courant. Heureusement, Postgres offre une belle façon de le faire avec une requête, en utilisant DISTINCT ON:

SELECT DISTINCT ON (site_id) 
    id, site_id, start, "end", duration 
FROM generator_logs 
ORDER BY site_id, start DESC; 

DISTINCT ON (site_id) signifie « retourner un enregistrement par site_id ». La clause order by détermine quel enregistrement est. Notez, cependant, que ceci est subtilement différent de votre requête d'origine - si vous avez deux enregistrements pour le même site avec le même start, votre requête retournera deux enregistrements, alors que cela n'en renvoie qu'un seul.

+0

Cela ne rapportera qu'une seule ligne par site_id, même si deux id_site partagent la même valeur (max) pour "start". – wildplasser

+0

@wildplasser Bien repéré. J'ai eu cela dans une édition précédente, jusqu'à ce que je me suis rendu compte que la requête originale ne fonctionne pas réellement (elle a un 'GROUP BY' au lieu d'un' WHERE 'dans la sous-requête). Je pense que c'était l'intention du demandeur ("Je veux sélectionner l'entrée la plus récente pour chaque site"). En tout cas, je l'ai remis. À votre santé! –

+0

L'OP n'est pas très clair sur ses intentions. Le 'GROUP BY' dans la sous-requête est complètement inutile, cela fonctionne même étrangement: les enregistrements qui correspondent à 'MAX()' ** d'un autre groupe ** seront également sélectionnés. – wildplasser

0

Une façon de trouver des documents ayant la valeur MAX par groupe est de sélectionner les enregistrements pour lesquels il n'y a pas d'enregistrement dans le même groupe ayant une valeur plus élevée:

SELECT id, site_id, "start", "end", duration 
FROM generator_logs g1 
WHERE NOT EXISTS (
    SELECT 1 
    FROM generator_logs g2 
    WHERE g2.site_id = g1.site_id 
    AND g2."start" > g1."start" 
    ); 
Questions connexes