2009-03-04 7 views
1

J'ai rencontré un problème plutôt étrange. J'ai les données d'exemple suivantes pour travailler dans une base de données MySQL:SQL: Nécessité de limiter le jeu de résultats en fonction d'une sous-requête

 
    |key| data| index | total | timestamp   | 
    | # | a | 1  | 2  | 2009-01-02 01:01:32 | 
    | $ | b | 2  | 2  | 2009-01-02 01:03:32 | 
    | % | c | 1  | 3  | 2009-01-03 01:01:32 | 
    |^| d | 2  | 3  | 2009-01-03 01:04:32 | 
    | & | e | 3  | 3  | 2009-01-03 01:02:32 | 
    | * | f | 1  | 2  | 2009-01-05 01:01:32 | 

Qu'est-ce qui se passe est qu'un autre processus (pas sous mon contrôle) reçoit des paquets de données, et de les stocker directement dans la base de données avec un horodatage pour heure d'arrivée. Les paquets sont censés arriver en rafale ... a, b arriveront près l'un de l'autre et sont indexés 1 et 2, chaque paquet contenant le nombre "total" de paquets transmis. La clé est une clé primaire auto-incrémentée normale. Ce dont j'ai besoin est une vue qui affichera la liste la plus récente qui est arrivée (liste partielle, si tous les paquets sont arrivés, est acceptable).

Pour la requête ci-dessus, le résultat devrait idéalement être "f", mais je ne vois pas de moyen de le faire. Si nous ne pouvons pas l'obtenir d'une autre manière, retourner "a" et "f" serait acceptable. En d'autres termes, une petite quantité de données supplémentaires capturées par l'instruction select n'est pas un gros problème. Pour la période précédant l'arrivée de «f», le bon retour est c, d et e.

Mes pensées générales ont été le long des lignes de:

 
SELECT * FROM table WHERE total = (
    SELECT total FROM table WHERE timestamp = (
     SELECT MAX(timetamp) FROM table 
    ) 
) 
ORDER BY DESC timestamp 
LIMIT (
    SELECT total FROM table WHERE timestamp = (
     SELECT MAX(timetamp) FROM table 
) 

Comme certains d'entre vous ont probablement remarqué, vous ne pouvez pas faire une sous-requête dans la clause LIMIT (au moins avec MySQL). Quelqu'un at-il une autre approche pour résoudre ce problème? La requête ci-dessus peut être rendue beaucoup plus propre en imbriquant un JOIN à une petite liste d'identifiants récents, mais cela laisse toujours le problème LIMIT-subquery dans la sous-requête.

En deux requêtes de la scène, ce qui est relativement trivial. Le problème est qu'il doit devenir l'instruction select de définition pour une vue.

Modifier pour corriger mauvais exemple sql

+0

Pouvez-vous essayer d'expliquer clairement ce qui constitue «le plus récent»? Puisque vous dites que "F" et "A" seraient acceptables, cela ne semble pas être un horodatage direct. –

+0

J'assumais une approche dans laquelle vous garantissiez des valeurs "index" uniques, en vous assurant de ne pas retourner plus de lignes "totales". Fondamentalement disant que, bien qu'il soit critique pour "f" d'être retourné, retourner "a" aussi ne serait pas un défaut fatal. – user73917

+0

Il est dommage qu'il n'y ait pas d'identifiant pour chaque collection, si vous avez une collection sur 2 jours, ou si deux collections se chevauchent, vous allez avoir des problèmes. – Adam

Répondre

1

requête Je suggère:

SELECT * 
FROM packets 
WHERE total = (SELECT total 
       FROM packets 
       WHERE timestamp = (SELECT MAX(timestamp) FROM packets)) 
    AND timestamp >= (SELECT MAX(timestamp) FROM packets WHERE idx = 1) 
ORDER BY timestamp DESC; 

Inaction:

mysql> create table packets(id bigint(20) AUTO_INCREMENT primary key, data char(1), idx int(10), total int(10), timestamp datetime); 
Query OK, 0 rows affected (0.00 sec) 

mysql> insert into packets(data, idx, total, timestamp) values('a', 1 ,2,'2009-01-02 01:01:32'), 
    ->  ('b' ,2 ,2,'2009-01-02 01:03:32'), 
    ->  ('c' ,1 ,3,'2009-01-03 01:01:32'), 
    ->  ('d' ,2 ,3,'2009-01-03 01:04:32'), 
    ->  ('e' ,3 ,3,'2009-01-03 01:02:32'), 
    ->  ('f' ,1 ,2,'2009-01-05 01:01:32'); 
Query OK, 6 rows affected (0.00 sec) 
Records: 6 Duplicates: 0 Warnings: 0 

mysql> SELECT * 
    -> FROM packets 
    -> WHERE total = (SELECT total 
    -> FROM packets 
    -> WHERE timestamp = (SELECT MAX(timestamp) FROM packets)) 
    -> AND timestamp >= (SELECT MAX(timestamp) FROM packets WHERE idx = 1) 
    -> ORDER BY timestamp DESC; 
+----+------+------+-------+---------------------+ 
| id | data | idx | total | timestamp   | 
+----+------+------+-------+---------------------+ 
| 6 | f | 1 |  2 | 2009-01-05 01:01:32 | 
+----+------+------+-------+---------------------+ 
1 row in set (0.00 sec) 

mysql> delete from packets where id = 6; 
Query OK, 1 row affected (0.00 sec) 

mysql> SELECT * FROM packets WHERE total = (SELECT total FROM packets WHERE timestamp = (SELECT MAX(timestamp) FROM packets)) AND timestamp >= (SELECT MAX(timestamp) FROM packets WHERE idx = 1) ORDER BY timestamp DESC; 
+----+------+------+-------+---------------------+ 
| id | data | idx | total | timestamp   | 
+----+------+------+-------+---------------------+ 
| 4 | d | 2 |  3 | 2009-01-03 01:04:32 | 
| 5 | e | 3 |  3 | 2009-01-03 01:02:32 | 
| 3 | c | 1 |  3 | 2009-01-03 01:01:32 | 
+----+------+------+-------+---------------------+ 
3 rows in set (0.00 sec) 

mysql> 
+0

Non ... mais je peux voir pourquoi la question était confuse ... Edité pour ajouter "Pour la période avant l'arrivée de" f ", le bon retour est c, d et e." – user73917

+0

J'ai mis à jour la requête – sfossen

+0

notez que, avant l'arrivée de f, cela ne retournera que "d", car seul le timestamp de s correspondra. (les paquets n'arrivent pas dans l'ordre, ou tout à la fois) – user73917

0

S'ils arrivent pour sans autres paquets en cours d'écriture entre les deux, suivant devrait également fonctionner.

SELECT * 
FROM Total t 
    INNER JOIN (
     SELECT Total, Timestamp 
     FROM Total t 
      INNER JOIN (
       SELECT Timestamp = MAX(Timestamp) 
       FROM Total 
       WHERE ID = 1 
      ) ts ON ts.Timestamp = t.Timestamp. 
    ) tit ON tit.Total = t.Total AND tit.Timestamp <= t.Timestamp 
+0

Je peux finir par aller avec une approche comme celle-ci. Malheureusement, parfois, ces horodatages peuvent être décalés de plusieurs heures. Pour les curieux, il s'agit de données scientifiques de télédétection utilisant un tuyau très intermittent. – user73917

+0

@kiruwa, si tel est le cas, vous ne pouvez pas construire de manière fiable une requête pour obtenir toutes les données de la dernière transmission. Si deux transmissions avec des totaux identiques arrivent avec plusieurs heures d'intervalle entre ses paquets, vous êtes condamné ... condamné je dis –

+0

Ouais, la tentative était juste pour obtenir quelque chose de près. L'équivalent de la requête en deux étapes: foo = SELECT total à partir de la table WHERE timestamp = (SELECT MAX (timestamp) ...) SELECT * FROM table WHERE total = $ foo ORDER BY DESC horodatage LIMIT foo – user73917

0

Voici comment je le ferais en serveur SQL, vous pouvez convertir en syntaxe mysql.

SELECT * 
FROM table 
    INNER JOIN (SELECT TOP 1 * FROM table ORDER BY key DESC) AS t ON (table.timestamp = t.timestamp AND table.total = t.total) 
0

J'ai fini par un formulaire de requête légèrement différente:

 
CREATE VIEW NewestTimestamps AS 
    SELECT index, MAX(timestamp) AS maxTS FROM table GROUP BY index; 

CREATE VIEW NewestList AS 
    SELECT * FROM table AS t 
    JOIN NewestTimestamps sub ON t.timestamp = sub.maxTS AND sub.index = t.index 
    WHERE t.total = (SELECT t2.total FROM table AS t2 
    WHERE timestamp = (SELECT MAX(timestamp) FROM table) 
); 

Cette liste est pas exactement ce que je demandais, mais il semble effectivement impossible de distinguer de manière fiable des données plus récentes et plus. Au lieu de cela, cela me donnera l'élément le plus récent à l'index 1, puis l'index 2, etc ... En outre, la clause WHERE limitera la taille de la vue à la taille de la file d'attente la plus récente.

Notez que la première vue est requise car mysql n'autorise pas les sous-requêtes dans la clause FROM dans une vue.

Questions connexes