2009-09-04 5 views
0

J'ai une table qui a laun peu complexe mySQL Interrogation

suivante
id  timestamp 
------------------------------------- 
1  1247046037 
4  1247047437 
5  1247047438 
6  1247048738 

Maintenant, je veux retourner tous ces ids qui sont à 3 minutes en dehors de l'ID précédent. Dans l'exemple ci-dessus, il devrait retourner 1,4,6.

Comment dois-je procéder? Un peu confus. Je veux éviter les jointures et tout le reste autant que possible.

MISE À JOUR Désolé, j'oublié de mentionner que les ID ne peuvent pas être séquentielle ne peut donc pas utiliser id = id + 1

Répondre

2

Tu ne vas pas être en mesure de le faire sans jointures, autant que possible dire. Cela devrait faire ce que vous avez besoin

select t1.id 
from mytable as t1 
    left join mytable as t2 on t1.id = (t2.id + 1) 
where t2.id is null or (t1.timestamp - 180) > t2.timestamp 

Si le champ id peut avoir des lacunes, vous aurez besoin d'introduire un champ de numéro de ligne pour ce faire. Probablement plus efficace pour ajouter un champ autonumber à votre table si vous l'exécutez régulièrement.

select t1.id 
from 
    (select mytable.*, @rownum:[email protected]+1 as rownum from mytable, (select @rownum:=0) r order by id) as t1 
    left join 
    (select mytable.*, @rownum2:[email protected]+1 as rownum from mytable, (select @rownum2:=0) r order by id) as t2 on t1.rownum = (t2.rownum + 1) 
where t2.id is null or (t1.timestamp - 180) > t2.timestamp 
+0

j'ai mis à jour ma question –

+0

réponse mis à jour Accommo datez les trous dans le champ id. – Richard

0

Vous pouvez utiliser un auto-jointure nul pour obtenir deux pièces d'identité à côté de l'autre:

SELECT thing2.id 
FROM things AS thing1 
JOIN things AS thing2 ON thing1.id<thing2.id 
LEFT JOIN things AS nothing ON nothing.id BETWEEN thing1.id+1 AND thing2.id-1 
WHERE nothing.id IS NULL 
AND thing2.timestamp=thing1.timestamp+180 

ie. il n'y a pas de ligne avec un identifiant entre thing1 et thing2.

(Cette obtient des lignes qui sont exactement 3 minutes d'intervalle, on ne sait pas de la question de savoir si vous voulez vraiment les lignes qui sont 3 ou-minutes ou 3 ou-moins de minutes d'intervalle.)

+0

change = to> = pour le faire fonctionner – van

1

Ce serait fonctionne parfaitement dans MSSQL. Ce serait intéressant de voir si MySQL peut gérer cette requête départage:

SELECT  curr.* 
      --//,prev.* 
      --//,curr."timestamp" - prev."timestamp" 
FROM  @Table curr 
LEFT JOIN @Table prev 
     ON prev.ID = (SELECT TOP 1 prev_match.ID 
         FROM  @Table prev_match 
         WHERE  prev_match.ID < curr.ID 
         ORDER BY prev_match.ID DESC) 
WHERE  curr."timestamp" - prev."timestamp" >= 180 
     OR prev.ID IS NULL --// cover special case when there is no previous at all 

Mais si vous laissez sans avoir la ligne avec id=0, je supprimerait la OU de condition WHERE et changer LEFT JOIN en INNER JOIN

+0

'TOP 1' est la syntaxe spécifique à MSSQL; La version de MySQL et Postgres est 'LIMIT 1' à la fin. Mais MySQL ne peut pas LIMITER sur une sous-requête, malheureusement. – bobince

+0

Pouvez-vous faire 'SELECT MAX (prev_match.ID) ...' dans la sous-requête à la place? – van

+0

Oui, ça devrait marcher! Pourquoi je n'y ai pas pensé? – bobince

0

Si vous voulez éviter une jointure, je pense que vous pouvez utiliser à la place des sous-requêtes, mais je pense que ce soit serait plus lent donc recommander contre:

SELECT t.id 
FROM yourTable t 
WHERE t.timestamp - 
(SELECT t3.timestamp --This gives the time of the id before 
    FROM yourTable t3 
    WHERE id = 
    (SELECT ISNULL(MAX(t2.id),-1) --This gives the id before or -1 if first id. 
    FROM yourTable t2 
    WHERE t2.id < t.id) 
) 
> 180