2012-09-03 7 views
1

J'essaie de résoudre un problème intéressant. J'ai une table qui a, entre autres données, ces colonnes (dates de cet échantillon sont présentés au format européen - jj/mm/aaaa):Sélectionner des valeurs uniques triés par date

n_place_id dt_visit_date 
(integer) (date) 
========== ============= 
    1   10/02/2012 
    3   11/03/2012 
    4   11/05/2012 
    13   14/06/2012 
    3   04/10/2012 
    3   03/11/2012 
    5   05/09/2012 
    13   18/08/2012 

Fondamentalement, chaque lieu peut être visité à plusieurs reprises - et les dates peuvent être dans le passé (visites terminées) ou dans le futur (visites planifiées). Par souci de simplicité, les visites d'aujourd'hui font partie des futures visites planifiées.

Maintenant, je dois exécuter une sélection sur cette table, qui rassemblerait ID unique place de cette table (sans date) trié dans l'ordre suivant:

  1. visites futures vont avant les visites passées
  2. visites futures ont la priorité dans le tri sur les visites passées pour la même place
  3. pour vos prochaines visites, la première date doit avoir la priorité dans le tri pour le même endroit
  4. pour les visites passées, la dernière date doit tak La préséance dans le tri pour le même endroit.

Par exemple, pour les données d'échantillons présentés ci-dessus, le résultat que je besoin est:

5  (earliest future visit) 
3  (next future visit into the future) 
13  (latest past visit) 
4  (previous past visit) 
1  (earlier visit in the past) 

Maintenant, je peux obtenir le désiré de tri en utilisant case when dans la clause order by comme ceci:

select 
    n_place_id 
from 
    place_visit 
order by 
    (case when dt_visit_date >= now()::date then 1 else 2 end), 
    (case when dt_visit_date >= now():: date then 1 else -1 end) * extract(epoch from dt_visit_date) 

Ce sorte de fait ce dont j'ai besoin, mais il contient des ID répétés, alors que j'ai besoin d'ID uniques. Si j'essaie d'ajouter distinct à l'instruction select, postgres se plaint que je dois avoir le order by dans la clause select - mais alors l'unique ne sera plus raisonnable, car j'ai des dates là-bas. D'une manière ou d'une autre, je pense qu'il devrait y avoir un moyen d'obtenir le résultat dont j'ai besoin dans une instruction select, mais je n'arrive pas à comprendre comment le faire.

Si cela ne peut pas être fait, alors, bien sûr, je vais devoir faire le tout dans le code, mais je préférerais avoir cela dans une déclaration SQL.

P.S. Je ne m'inquiète pas de la performance, car le jeu de données que je vais trier n'est pas important. Après l'application de la clause where, il contiendra rarement plus d'une dizaine d'enregistrements.

Répondre

1

Essayez cette

select n_place_id 
from 
(
    select *, 
    extract(epoch from (dt_visit_date - now())) as seconds, 
    1 - SIGN(extract(epoch from (dt_visit_date - now()))) as futurepast 
    from #t 
) v 
group by n_place_id 
order by max(futurepast) desc, min(abs(seconds)) 
+0

J'obtiens l'erreur 'ne peut pas lancer intervalle integer' en ligne' casting ((dt_visit_date - maintenant()) comme int) comme days' –

+0

Essayez le modifier ci-dessus – podiluska

+0

Eh oui! C'est à peu près tout. Il suffit de changer 'SIGN (...)' '1 - SIGN (...)' - et ça a fait l'affaire. Merci beaucoup! –

2

Avec DISTINCT ON, vous pouvez facilement afficher des colonnes supplémentaires de la ligne avec le résultat n_place_id:

SELECT n_place_id, dt_visit_date 
FROM (
    SELECT DISTINCT ON (n_place_id) * 
     ,dt_visit_date < now()::date AS prio -- future first 
     ,@(now()::date - dt_visit_date) AS diff -- closest first 
    FROM place_visit 
    ORDER BY n_place_id, prio, diff 
    ) x 
ORDER BY prio, diff; 

Effectivement je prends la ligne avec la première date future (y compris « aujourd'hui ") par n_place_id - ou la dernière date dans le passé, à défaut.
Ensuite, les lignes uniques résultantes sont triées selon les mêmes critères.

  • FALSE sortes avant TRUE
  • Le "absolute value" @ aide à trier "le plus proche d'abord"
  • Plus sur le DISTINCT ON spécifique Postgres dans cette réponse connexe.

Résultat:

n_place_id | dt_visit_date 
------------+-------------- 
5   | 2012-09-05 
3   | 2012-10-04 
13   | 2012-08-18 
4   | 2012-05-11 
1   | 2012-02-10 
Questions connexes