Marca de tiempo máxima en cada día

Tengo una tabla de eventos:

create table event (id integer primary key, date timestamptz unique);
insert into event (id, date) values
 (22784, '2012-01-01 01:00:00+00'),
 (22785, '2012-01-01 04:00:00+00'),
 (22786, '2012-01-01 07:00:00+00'),
 (22787, '2012-01-01 10:00:00+00'),
 (22788, '2012-01-01 13:00:00+00'),
 (22789, '2012-01-01 16:00:00+00'),
 (22790, '2012-01-01 19:00:00+00'),
 (22791, '2012-01-01 22:00:00+00'),
 (22792, '2012-01-02 01:00:00+00'),
 (22793, '2012-01-02 04:00:00+00'),
 (22794, '2012-01-02 07:00:00+00'),
 (22795, '2012-01-02 10:00:00+00'),
 (22796, '2012-01-02 13:00:00+00'),
 (22797, '2012-01-02 16:00:00+00'),
 (22798, '2012-01-02 19:00:00+00'),
 (22799, '2012-01-02 22:00:00+00'),
 (22800, '2012-01-03 01:00:00+00'),
 (22801, '2012-01-03 04:00:00+00'),
 (22802, '2012-01-03 07:00:00+00'),
 (22803, '2012-01-03 10:00:00+00'),
 (22804, '2012-01-03 13:00:00+00'),
 (22805, '2012-01-03 16:00:00+00'),
 (22806, '2012-01-03 19:00:00+00'),
 (22807, '2012-01-03 22:00:00+00'),
 (22808, '2012-01-04 01:00:00+00'),
 (22809, '2012-01-04 04:00:00+00'),
 (22810, '2012-01-04 07:00:00+00'),
 (22811, '2012-01-04 10:00:00+00'),
 (22812, '2012-01-04 13:00:00+00'),
 (22813, '2012-01-04 16:00:00+00'),
 (22814, '2012-01-04 19:00:00+00'),
 (22815, '2012-01-04 22:00:00+00'),
 (22816, '2012-01-05 01:00:00+00'),
 (22817, '2012-01-05 04:00:00+00'),
 (22818, '2012-01-05 07:00:00+00'),
 (22819, '2012-01-05 10:00:00+00'),
 (22820, '2012-01-05 13:00:00+00'),
 (22821, '2012-01-05 16:00:00+00'),
 (22822, '2012-01-05 19:00:00+00'),
 (22823, '2012-01-05 22:00:00+00'),
 (22824, '2012-01-06 01:00:00+00'),
 (22825, '2012-01-06 04:00:00+00'),
 (22826, '2012-01-06 07:00:00+00'),
 (22827, '2012-01-06 10:00:00+00'),
 (22828, '2012-01-06 13:00:00+00'),
 (22829, '2012-01-06 16:00:00+00'),
 (22830, '2012-01-06 19:00:00+00'),
 (22832, '2012-01-06 22:00:00+00'),
 (22833, '2012-01-07 01:00:00+00'),
 (22834, '2012-01-07 04:00:00+00'),
 (22835, '2012-01-07 07:00:00+00'),
 (22836, '2012-01-07 10:00:00+00'),
 (22837, '2012-01-07 13:00:00+00'),
 (22838, '2012-01-07 16:00:00+00'),
 (22839, '2012-01-07 19:00:00+00'),
 (22840, '2012-01-07 22:00:00+00'),
 (22841, '2012-01-08 01:00:00+00'),
 (22842, '2012-01-08 04:00:00+00'),
 (22843, '2012-01-08 07:00:00+00'),
 (22844, '2012-01-08 10:00:00+00'),
 (22845, '2012-01-08 13:00:00+00'),
 (22846, '2012-01-08 16:00:00+00'),
 (22847, '2012-01-08 19:00:00+00'),
 (22848, '2012-01-08 22:00:00+00'),
 (22849, '2012-01-09 01:00:00+00'),
 (22850, '2012-01-09 04:00:00+00'),
 (22851, '2012-01-09 07:00:00+00'),
 (22852, '2012-01-09 10:00:00+00'),
 (22853, '2012-01-09 13:00:00+00'),
 (22854, '2012-01-09 16:00:00+00'),
 (22855, '2012-01-09 19:00:00+00'),
 (22856, '2012-01-09 22:00:00+00'),
 (22857, '2012-01-10 01:00:00+00'),
 (22858, '2012-01-10 04:00:00+00'),
 (22859, '2012-01-10 07:00:00+00'),
 (22860, '2012-01-10 10:00:00+00'),
 (22861, '2012-01-10 13:00:00+00'),
 (22862, '2012-01-10 16:00:00+00'),
 (22863, '2012-01-10 19:00:00+00'),
 (22864, '2012-01-10 22:00:00+00')
;

Quiero obtener la identificación de la marca de tiempo máxima de cada día. lo que tengo es:

select date::date as date, id
from event e
where date = (
    select max(date)
    from event
    where date_trunc('day', date) = date_trunc('day', e.date)
)
order by date;
    date    |  id   
------------+-------
 2011-12-31 | 22784
 2012-01-01 | 22792
 2012-01-02 | 22800
 2012-01-03 | 22808
 2012-01-04 | 22816
 2012-01-05 | 22824
 2012-01-06 | 22833
 2012-01-07 | 22841
 2012-01-08 | 22849
 2012-01-09 | 22857
 2012-01-10 | 22864
(11 rows)

Funciona como se esperaba, pero el rendimiento es muy malo. ¿Alguna sugerencia?

preguntado el 12 de junio de 12 a las 18:06

+1 por cierto. Una pregunta claramente formulada, datos de prueba perfectamente utilizables. ¡Asi es como lo haces! -

3 Respuestas

DISTINCT ON podría funcionar mejor:

SELECT DISTINCT ON (d) date_trunc('day',date)::date as d ,id
FROM event 
ORDER BY d desc, date desc;

Si hay que ordenar los resultados por fechas ascendentes, es necesario otro nivel de ordenación:

SELECT d,id 
FROM (
    SELECT DISTINCT ON (d) date_trunc('day',date)::date as d ,id
    FROM event 
    ORDER BY d desc, date desc
) s
ORDER BY d;

Respondido el 12 de junio de 12 a las 22:06

¡Muy rapido! 802 ms versus 314 segundos con el mío. Si nadie muestra algo sorprendentemente rápido, esta será la respuesta. Esta técnica será muy útil en un par de otros lugares. - Clodoaldo Neto

Bien, me pregunto qué sucede cuando agregas el índice que sugerí. - tony hopkinson

@Clodoaldo: Este caso funciona sin calificación de tabla porque Daniel cambió el nombre de la columna de salida a d para evitar un conflicto de nombres. - Erwin Brandstetter

Acerca de la columna de salida: como dijo Erwin. La necesidad de calificar se aplica a su respuesta, no a la mía. Sin embargo, estoy de acuerdo con el EXCESO DE PEDIDO en mi respuesta. Utilizando ORDER BY d ASC en primer lugar, el nivel superior de ordenación puede y debe eliminarse. - Daniel Vérité

@DanielVérité Ok, ya veo. Eliminó el comentario. - Clodoaldo Neto

Select date,id from event
inner join (
    Select Max(date_trunc('day', date) as LatestDay 
    From event
) On LatestDay = date_trunc('day', date)

Podría funcionar un poco mejor, pero es la función date_trunc la que te está matando. Esto podría valer la pena intentarlo.

CREATE INDEX ON event ((date_trunc('day',date)));

Respondido el 12 de junio de 12 a las 18:06

Tu consulta muestra que no entendiste la pregunta. Pero +1 por la sugerencia de índice. - Clodoaldo Neto

Esto debería ser considerablemente más rápido:

SELECT DISTINCT ON (1)
       date::date, id
FROM   event e
ORDER  BY 1, e.date DESC;  -- e.date, not just date!
  • No es necesario otro nivel de ordenación para obtener un orden ascendente.

  • date_trunc() no es necesario. Casting hasta la fecha hace el trabajo.

  • En los ORDER BY cláusula, asegúrese de calificar la tabla e.date para seleccionar la columna de origen, no la columna de salida del mismo nombre. Las reglas de visibilidad se encuentran las confuso aquí, gracias al desordenado estándar SQL.

  • Por cierto, date es un palabra reservada en cada estándar SQL. No vas a usar eso como nombre de columna en el mundo real, ¿verdad? Ya es confuso en el pequeño ejemplo.


Otra forma sería con una función de ventana:

SELECT DISTINCT ON (1)
       date::date
      ,first_value(id) OVER (PARTITION BY date::date ORDER BY date DESC) AS id
FROM   event
ORDER  BY 1;

Más legible, pero un poco más lento.

Respondido el 15 de junio de 12 a las 17:06

Sí la calificación de la tabla en el order by cláusula es necesaria para que sea correcta. - Clodoaldo Neto

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.