2011-09-16 4 views
0

J'ai une requête à écrire c'est un peu trop compliqué pour moi de comprendre. L'aide serait appréciée. Ma table est, en substance:Requête de doctrine semi-compliquée

Table: Foo 
Columns: id 
     timestamp 
     bar1 
     bar2 
     bar3 

Bar1, bar2 et bar3 ne sont pas uniques pour chaque enregistrement. En fait, la combinaison des 3 relie spécifiquement plusieurs enregistrements. Par exemple:

0 | 2011-01-01 00:01:01 | 100 | 5 | 'Hello' 
1 | 2011-01-01 12:12:00 | 100 | 5 | 'Hello' 
2 | 2011-01-01 07:43:00 | 101 | 8 | 'Monkey' 
3 | 2011-01-01 17:46:08 | 102 | 9 | 'Cat' 
4 | 2011-01-01 23:15:00 | 100 | 5 | 'Hello' 
5 | 2011-01-01 10:00:00 | 100 | 6 | 'Goodbye' 

Enregistrements 0, 1 et 4 sont liés tandis que 2, 3 et 5 sont uniques.

Dans cet esprit, je voudrais trouver tous les enregistrements bar1 == 100, bar2 == 5 et bar3 == « Bonjour », SI tous les enregistrements du groupe ont un horodatage < = l'heure actuelle OU l'un des enregistrements du groupe a un horodatage < = à l'heure actuelle - 30 minutes.

J'espère que cela a du sens. Si ce n'est pas le cas, faites le moi savoir et je ferai de mon mieux pour clarifier.

Modifier: J'espérais que d'avoir juste la barre serait suffisante pour un exemple, mais en réalité, ma table réelle a plusieurs colonnes qui doivent correspondre pour indiquer un dossier est lié. J'ai mis à jour l'exemple en conséquence.

+0

Je ne connais pas vos données, mais il semble que {bar1, bar2, bar3} forment un groupe qui pourrait être "normalisé" dans une table séparée. – wildplasser

Répondre

1

Si je comprends bien votre correcty question, vous WANT tous les dossiers pour (bar1, bar2, BAR3) = (100, 5, 'Bonjour') et vous AVONS un certain current_time comme le seul critère d'entrée. Dans un premier temps, je reformule les deux conditions: Pour récupérer les membres d'un groupe, l'horodatage le plus important du groupe doit être dans le passé (votre première condition) ou le plus petit horodatage du groupe doit avoir au moins 30 minutes (équivalent à votre deuxième condition).

vous pourrez voir le groupe et ses horodateurs avec cette requête:

SELECT bar1, bar2, bar3, max(timestamp) group_end, min(timestamp) group_start 
     FROM foo 
     GROUP BY bar1, bar2, bar3; 

Ajout des filtres: (notez la syntaxe spéciale de psql pour \set et : - c'est seulement pour les tests)

\set current_time '''2011-01-01 17:00:00''' 
    SELECT bar1, bar2, bar3, max(timestamp) group_end, min(timestamp) group_start 
     FROM foo 
     GROUP BY bar1, bar2, bar3 
     HAVING max(timestamp) <= :current_time OR min(timestamp) +'30min' <= :current_time 

Cela ne retournera rien pour current_time = 2011-01-01 00:31:00 car aucun groupe n'est terminé et aucun ID de groupe inachevé n'est assez ancien. Après que le groupe (100, 5, Bonjour) est renvoyé en raison de la clause ou et l'autre groupe sont renvoyés tout en augmentant encore current_time.

Jusqu'à présent, si bien - la dernière pièce est de récupérer les membres de tous les groupes trouvés:

SELECT * FROM foo WHERE (bar1, bar2, bar3) in (
     SELECT bar1, bar2, bar3 FROM foo 
     -- add WHERE clause with constraints based on bar1, bar2, bar3 here 
     GROUP BY bar1, bar2, bar3 
     HAVING max(timestamp) <= :current_time OR min(timestamp) +'30min' <= :current_time 
    ) 
    -- add further constraints here 

Si ma première hypothèse sur votre paramters d'entrée sont erronées, vous pouvez bien sûr ajouter le filtre à chaque étape appropriée de la requête. Filtrer dans les étapes précédentes sera plus performant bien sûr. J'ai ajouté des commentaires de marqueurs appropriés.

0

Je ne suis pas très familier avec postgresql, mais voici une réponse dans T-SQL qui devrait renvoyer les résultats que vous recherchez. Je ne pense pas qu'il utilise des commandes qui ne sont pas disponibles dans postgresql mais je ne suis pas certain. En outre, il peut s'étouffer sur DISTINCT dans la sous-sélection, si c'est le cas, supprimez-le car ce n'est pas nécessaire. Bonne chance.

SELECT * 
FROM Foo 
WHERE 
(
bar IN (SELECT bar 
     FROM (SELECT bar, MAX(timestamp) As HighestTime FROM Foo GROUP BY bar) HT 
     WHERE HT.HighestTime <= GETDATE()) 
OR bar IN (SELECT DISTINCT bar FROM Foo WHERE timestamp <= DATEADD(mm, -30, GETDATE()) 
) 
AND 
bar = 100 

Le premier « bar » utilise une sous-sélection pour obtenir uniquement les numéros de bar qui ont tous horodatages inférieure ou égale à la datetime actuelle en cochant simplement le plus grand.

La seconde "barre d'entrée" utilise une sous-sélection pour obtenir les numéros de barre qui ont au moins 1 enregistrement avec un horodatage inférieur ou égal au temps actuel - 30 minutes.

Le "bar =" limite juste à un numéro de barre spécifique.

+0

Comment ajusteriez-vous ceci si la barre était en réalité bar1, bar2, bar3? – clang1234