Ir al contenido principal

Consultas SQL basadas en fecha y/o hora

Manejando SQL Server 2005, me encontre que tratando hacer consultas basadas en fechas, obtenía como resultado nada (empty). Esto se debía a que no tenía claro como trabajan los tipos de dato datetime y smalldatetime. Estos dos tipos de datos son muy similares, se diferencia en que datetime es más detallado a la hora de almacenar la fecha. Estas diferencias pueden ser vistas en la siguiente tabla:




















Tipo


Minimo


Maximo


Precision


datetime


Ene 1, 1753
media-noche

Dic 31, 9999 23:59:59.997
(0.003 segundos hasta la media-noche)

Más cercano
3.33 millisegundos

smalldatetime


Ene 1, 1900
media-noche

Jun 6, 2079 23:59
(un minuto hasta la media-noche)

Más cercano al mínuto


Ambos, representan una fecha y hora que es igual al número de días en relación a una fecha base. En SQL Server la fecha base es la medianoche del 1 de Enero de 1900. Observando la tabla, el tipo de dato smalldatetime, puede solo representar fechas desde esta fecha base, en cambio el datetime puede representar fechas antes del 1 de enero de 1900. Para hacer esto, el almacena el valor como número negativo.

Para visualizar, como estos valores son guardados.
Piensa en un valor decimal, cuya parte entera representa los días desde la fecha base y la parte fraccional (los numeros después del punto) representan la hora. Por ejemplo, el valor date/time mediodía del 4 de enero de 1900, es almacenado como 3.5, donde 3, representa tres días desde la fecha base y el 0.5, representa la mitad del día. Para ver esto en el SQL Server, ejecuta la siguiente consulta:
SELECT CAST(CAST('1900-01-04 12:00' AS datetime) AS float)

Otro datito importante es saber que el SQL Server no provee un tipo de dato que solo almacene fecha u hora.  Es decir, que si tu almacenas un valor sin la hora (la parte fraccional) se le asigna el valor de cero (0). De igual forma, si tu almacenas un valor sin la fecha, la parte entera se le asigna automáticamente el valor de cero (0), que para la fecha significa 1 de enero de 1900. Para ejemplo, ejecuta la siguiente consulta:

SELECT CAST('1900-01-04' AS datetime), CAST('10:00' AS datetime)

el cual retornará el siguiente resultado:
1900-01-04 00:00:00.000         1900-01-01 10:00:00.000

Como Buscar Solo la Fecha

Para ilustrar usaremos la siguiente tabla:

ID  DateVal
--  -----------------------
1   2001-02-28 10:00:00.000
2   2002-02-28 13:58:32.823
3   2002-02-29 00:00:00.000
4   2002-02-28 00:00:00.000


 Si realizamos la siguiente consulta:
SELECT * FROM DateSample WHERE DateVal = '2002-02-28'

El resultado solo traerá la fila #4, esto se debe a que el SQL Server buscará los datos cuya hora sea 00:00:00 (medianoche). El valor literal buscado (28 de febrero de 2002) implicítamente se le agregará la hora antes en mención. Entonces, como podemos evitar este suceso y obtener todos los registros cuya fecha sea 28 de febrero de 2002? Lo haremos de la siguiente forma:

Si la consulta es ejecuta frecuentemente, tú debes basar tu búsqueda en un rango de fechas, como la siguiente:
SELECT * FROM DateSample WHERE DateVal BETWEEN '2002-02-28' AND '2002-02-28 23:59:59.997'

Nota: Ahora pensarás que yo soy un brutón porque debi usar como límite superior '2002-02-29' en vez de '2002-02-28 23:59:59.997'. Pués no, si haces esto, la fila #3 será parte del resultado de tu consulta.

Otra forma de obtener el resultado de manera más sencilla (la que yo recomiendo), es de la siguiente manera:
SELECT * FROM DateSample WHERE DateVal >= '2002-02-28' AND DateVal < '2002-02-29'

Como Buscar Solo por la Hora (How to search by time)

Para ilustrar, consideremos la siguiente tabla:

ID  TimeVal
--  -----------------------
1   2002-02-28 10:00:00.000
2   1900-01-01 13:58:32.823
3   1900-01-01 09:59:59.997
4   1900-01-01 10:00:00.000


Si usamos la siguiente consulta para extraer las filas cuya hora es igual a las 10:00 AM:
SELECT * FROM TimeSample WHERE TimeVal = '10:00:00'

solo conseguiremos la fila #4. La fila #1 no será parte del resultado, debido a que sucede lo mismo que con la fecha, pero en este caso el valor que es implicítamente agregado, es la fecha. En adición, la fila #3 no es parte del resultado porque es un valor super cerca, pero no igual a las 10:00 AM.
Para obviar la fecha, tu puedes crear una consulta que corte la parte entera del valor date/time, y solo deje la parte fraccional. Esta sería de la siguiente forma:

SELECT * FROM TimeSample WHERE TimeVal - CAST(FLOOR(CAST(TimeVal AS float)) AS datetime) = '10:00' 

Esta consulta nos devolvería la fila 1 y 4. Lamentablemente, no hay otra forma de conseguir este resultado sin usar esta combinación de formulas. Por tal motivo, hay que tomar mucho cuidado como se almacena la data datetime/smalldatetime que solo se usa para horas (time-only). Si tú necesitas realizar frecuentemente este tipo de búsqueda, te recomiendo que analices y reestructures el diseño de tu base de datos.

Si tú deseas hacer consultas basadas solo por horas para un intervalo de tiempo, bastaría con una simple consulta (query), esto; tomando en cuenta que se ha almacenado la data hora (time-only) consistentemente sin el componente fecha. La consulta sería algo como esto:

SELECT * FROM TimeSample WHERE TimeVal BETWEEN '09:59' AND '10:01'

o

SELECT * FROM TimeSample WHERE TimeVal > '09:59' AND TimeVal < '10:01' 

Estas dos consultas retornarían los datos de la fila #3 y #4.

Si los datos de hora (time-only) han sido guardados en la base de datos inconsistemente, entonces se necesitará que la consulta evite o excluya la parte fecha de estos. Por ejemplo, tu podrías usar una consulta como esta:

SELECT * FROM TimeSample WHERE TimeVal - CAST(FLOOR(CAST(TimeVal AS float)) AS datetime) > '09:59' AND TimeVal - CAST(FLOOR(CAST(TimeVal AS float)) AS datetime) < '10:01'


la cual nos devuelve las filas 1, 3 y 4. Recuerden que no podrías realizar correctamente la búsqueda sin estas funciones, cuando la data hora ha sido guardada inconsistentemente.

Nota: Toda esta información ha sido traducida del libro Murach's SQL for SQL Server del autor Bryan Syverson.

Comentarios

  1. [...] FROM tblMiTabla WHERE Fecha > '2008-05-01' AND Fecha < '2008-05-12' Paginas relacionadas: - http://rdgz.wordpress.com/2008/01/13...fecha-yo-hora/ - http://sql.manivesa.com/Tutoriales+S...nados/313.aspx Recuerda por favor marcar el post si te [...]

    ResponderEliminar
  2. wOooW!!! muchas gracias no sabes cuanto me ayudo este post con lo del date, me salvaste.

    Gracias por este gran aporte!

    ResponderEliminar
  3. Gracias por compartir tus conocimientos

    Excelente apunte

    Gracias

    ResponderEliminar
  4. hola tengo un proble que no he podido resolver, donde me preguntan la fecha separacion menor a un mes.
    "Listar todos los pacientes que hayan recibido un tratamiento de radioterapia y quimioterapia con separación menor a un mes. Además en este listado debe indicar por cada paciente las fechas en que se aplicaron estos tratamientos, además de sus nombres."

    ResponderEliminar
  5. De 20!! me salvaste gracias

    ResponderEliminar
  6. Hola.
    Muchas gracias por el artículo. Me sirvió para desatascarme.

    ResponderEliminar
  7. como resuelvo para poner un rango de horas por ejemplo
    una hora de confirmacion y una hora de salida
    y ver todas las horas que estan confirmadas 35 minutos antes que la hora de salida..................

    ResponderEliminar
  8. Gran post, muchas gracias!

    ResponderEliminar
  9. necesito hacer una busqueda por fecha o por hora
    es una consulta de paciente por hora

    ResponderEliminar
  10. quiero saber todos los paciente k me tocan por ejemplo a dicha hora

    ResponderEliminar
  11. Muchas gracias por su aportacion
    muy buena página, gracias a Dios que los encontré....

    ResponderEliminar
  12. Gracias, primera vez!!

    ResponderEliminar
  13. Muchas gracias por explicar esto!

    Me ayudo mucho además de que esta muy claro, de verdad muchas gracias.

    ResponderEliminar
  14. Gracias, la información me fue muy útil.

    ResponderEliminar
  15. Gracias Brenda!!!
    Disculpa la tardanza en responderte.
    La vida es corta para la cantidad de cosas que tenemos que hacer

    ResponderEliminar
  16. Me marca error en el signo "-"... en sql server 200...

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

AJAX: Redireccionar a una nueva página después de haber iniciado sesión

Estaba trabajando en una app, la cual quería que mostrará un mensaje cada vez que el usuario por error introducía el nombre de usuario o contraseña inválida. Bueno esto no fue díficil, ya que conozco como hacerlo. Pero lo que en ese momento no habia pensado era lo contrario, si el usuario introduce correctamente sus datos, no debo mostrar ningún mensaje. Lo que debo hacer es redireccionar al usuario a su pantalla inicial luego del logeo.

Ajax - Tedioso Error: "Could not complete the operation due to error c00ce514"

Yo tuve de 9:30 AM hasta las 2:00 PM del día de hoy tratando de resolver este bendito error. Busque en Google y nada, asi que me tome la molestía de resolver este problema. Revise las funciones Ajax habitual, donde uno hace el statechanged y luego el open del objeto XmlHttp. Estas son las mías (lo hago para dar una referencia más clara): xmlHttp.onreadystatechange = function() { stateChanged(xmlHttp, placeHolder) } xmlHttp.open(method, url, true); Y nada. Hasta que me pude dar cuenta que en mi caso el bendito error salia porque el mensaje que queria desplegar en el "Place Holder" (no se como decirlo en español), tenía tilde y la letra Ñ-ñ. Asi que al parecer el Ajax no se lleva bien con caracteres fuera del alfabeto gringo. Te tocará a ti, decirme si realmente esto es un bugs o existe alguna forma de configurar en cualquiera de las capaz involucradas en el funcionamiento del Ajax para que este problem no se presente.