¿Cómo comparar fechas en los campos de fecha y hora en Postgresql?

188

Me he enfrentado a un escenario extraño al comparar fechas en postgresql (versión 9.2.4 en windows). Tengo una columna en mi tabla que dice update_date con el tipo 'marca de tiempo sin zona horaria'. El cliente puede buscar en este campo con solo fecha (es decir, 2013-05-03) o fecha con hora (es decir, 2013-05-03 12:20:00). Esta columna tiene el valor como marca de tiempo para todas las filas actualmente y tiene la misma parte de fecha (2013-05-03) pero diferencia en parte de tiempo.

Cuando comparo esta columna, obtengo resultados diferentes. Como los siguientes:

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date < '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-04' -> results found

select * from table where update_date >= '2013-05-03' -> results found

Mi pregunta es cómo puedo hacer posible que la primera consulta obtenga resultados, quiero decir, ¿por qué la tercera consulta está funcionando pero no la primera?

¿Puede alguien ayudarme con esto? Gracias por adelantado.

usuario2866264
fuente

Respuestas:

278

@Nicolai tiene razón acerca de la transmisión y por qué la condición es falsa para cualquier dato. Supongo que prefieres la primera forma porque quieres evitar la manipulación de la fecha en la cadena de entrada, ¿correcto? no necesitas tener miedo:

SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);
solo alguien
fuente
¿Es esta sintaxis ( '2013-05-03'::datey '1 day'::interval) PostgreSQL específica?
Llama congelada
55
@FrozenFlame sí lo es. la sintaxis estándar sería CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)(IIRC). YMMV sobre la existencia y el comportamiento de DATEy INTERVAL.
Alguien
@FrozenFlame es correcto, la respuesta no funciona sin transmitir las cadenas a los tipos de fecha. Todavía falta un elenco. debe ::DATEagregarse a la primera parte de la cláusula
where
¿No WHERE update_date::date = '2013-05-03' funcionaría tan bien y tal vez un poco más legible?
MikeF
@MikeF OP dijo update_dateera timestamp without timezone. Supuse un índice en esa columna. su predicado no usaría ese índice.
solo alguien
46

Cuando compara update_date >= '2013-05-03'postgres, arroja valores al mismo tipo para comparar valores. Entonces su '2013-05-03' fue lanzado a '2013-05-03 00:00:00'.

Entonces, para update_date = '2013-05-03 14:45:00' su expresión será que:

'2013-05-03 14:45:00' >= '2013-05-03 00:00:00' AND '2013-05-03 14:45:00' <= '2013-05-03 00:00:00'

Esto es siempre false

Para resolver este problema, envíe update_date a date:

select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result
Nicolai
fuente
1
La conversión de cada elemento update_datede la tabla frente al valor único del parámetro de consulta es terriblemente ineficiente, y se asegura de que el servidor no pueda aprovechar los índices en esa columna. Estoy tentado a -1 esto.
solo alguien
3
Sí, estoy de acuerdo en que la conversión de cada valor es ineficiente y puede dar -1 para esta solución. Pero describí la razón del problema y di un ejemplo que demuestra el problema. Ahora el usuario 2866264 sabe por qué su consulta no devuelve las filas esperadas y decidirá qué solución exacta es mejor para su caso único.
Nicolai
@ Nicolai: Muchas gracias por tu respuesta. Funciona siguiendo tu respuesta. También gracias por la explicación.
usuario2866264
1
@Nicolai - Teniendo en cuenta lo que dijo sobre Postgres expandiendo la fecha literal a la medianoche, si el objetivo es encontrar registros marcados en una sola fecha (3 de mayo), ¿sería este código correcto y más eficiente? SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (Tenga en cuenta el uso del 4 de mayo en lugar de tercero y con un SIGNO MENOS QUE EN vez de menos que o igual.)
Basil Bourque
2

Use la conversión de fecha para comparar con la fecha: intente esto:

select * from table 
where TO_DATE(to_char(timespanColumn,'YYYY-MM-DD'),'YYYY-MM-DD') = to_timestamp('2018-03-26', 'YYYY-MM-DD')
Yenky Bustamante
fuente