domingo, 19 de septiembre de 2010

[ADO.NET] Filtrar rango de fechas

 

Introducción


Uno de los principales problemas que se pueden encontrar cuando se confecciona una consulta es el trabajo con campos del tipo fecha.

Por lo general estos campos no solo persisten la fecha, sino que también registran la hora.

Es por eso que ciertas consultas pueden no retornar los valores deseados, aunque se este definiendo correctamente los parámetros de la consulta.

 

Planteo del problema


Para demostrar el problema se plantea el log de actividades del usuario, en donde se registras las acciones que este tiene en la aplicación. Pero el problema se presenta cuando se necesita consultar estos registros para analizar las acciones realizadas en un determinado rango de días, por supuesto se quiere ver los días completo, y es aquí donde nos encontramos con el inconveniente.

Si se analiza los registros de la tabla se ve que el registro de la fecha no solo usa el día, sino también la hora.

imagen1

La aplicación de ejemplo cuenta con dos opciones de búsqueda, pero en este primer análisis nos centraremos en el primer botón de nombre “Buscar (sin usar CONVERT)”, al utilizarlo veremos el resultado de la imagen:

imagen2

Pero con solo comparar los registros obtenido con los que se encuentran en la tabla, nos damos cuenta que hay un problema, esta retornando menos filas de las esperadas, la pregunta seria, porque sucedió esto ?

El código utilizado para obtener los registro es el siguiente :

public static LogActividades.RegistroActividadesDataTable GetFilterByDateRange(DateTime desde, DateTime hasta)
{
    LogActividades.RegistroActividadesDataTable dt = new LogActividades.RegistroActividadesDataTable();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {
        conn.Open();

        string sql = @"SELECT Id, Descripcion, fecharegistro, usuario 
                        FROM RegistroActividades
                        WHERE fecharegistro >= @desde AND fecharegistro <= @hasta";

        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("@desde", desde);
        cmd.Parameters.AddWithValue("@hasta", hasta);

        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(dt);

    }

    return dt;
}

El parámetro de la fechas toma el valor directo de los controles que están en la pantalla, pero esta fecha aunque no se conozca también lleva consigo un componente de hora, que puede ser apreciado si se detiene el código y se analiza el valor:

imagen3

El tipo de dato DataTime, lleva una hora aunque esta no se especifique concretamente.

Esto aclara bastante el porque la consulta arroja menos ítems de los esperados, resulta que esta quitando aquellos registros en donde la hora sea menos a las 12 AM

 

Solución del problema


La solución al problema se obtiene usando una funciona en la query, que aplicada sobre los campos de fecha en el filtro quiten la componente de la hora. En realidad no remueve la hora, sino que la normaliza para que esta también tenga las 12 AM, por lo tanto el filtro no descartara ningún registro.

Para esto se hará uso de la función CONVERT, la cual permite convertir entre tipos de datos.

CAST y CONVERT (Transact-SQL)

Si se ejecuta una consulta usando esta función se podría apreciar lo comentado mas arriba:

imagen4

Las fechas de los registros dejan de tener la hora original, ahora todos presentan las 12 AM, esto es justamente lo que ayudara en el filtro.

Entonces si ahora se aplica lo comentado al código se obtiene el resultado esperado (usar el botón “Buscar (usando CONVERT)”):

imagen5

Para que esto resulte se utilizo el siguiente código:

public static LogActividades.RegistroActividadesDataTable GetFilterByDateRangeRemoveHour(DateTime desde, DateTime hasta)
{
    LogActividades.RegistroActividadesDataTable dt = new LogActividades.RegistroActividadesDataTable();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {
        conn.Open();

        string sql = @"SELECT Id, Descripcion, fecharegistro, usuario 
                        FROM RegistroActividades
                        WHERE CONVERT(smalldatetime, CONVERT(char(10), fecharegistro, 103), 103)  >= @desde 
                        AND CONVERT(smalldatetime, CONVERT(char(10), fecharegistro, 103), 103) <= @hasta";

        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("@desde", desde);
        cmd.Parameters.AddWithValue("@hasta", hasta);

        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(dt);
    }

    return dt;
}

El uso del CONVERT aplicado en los campo de “fecharegistro”, es quien quita la componente de la hora y permite aplicar el filtro correctamente.

 

Alternativa usando Between


Como alternativa al método anterior se podría lograr usando el Between en la query para filtrar por el rango de fechas.

En el Form2 del ejemplo se podrá encontrar la implementación de este caso.

public static LogActividades.RegistroActividadesDataTable GetFilterByDateRangeWithBetween(DateTime desde, DateTime hasta)
{
    LogActividades.RegistroActividadesDataTable dt = new LogActividades.RegistroActividadesDataTable();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {

        string sql = @"SELECT Id, Descripcion, fecharegistro, usuario 
                        FROM RegistroActividades
                        WHERE CAST(CONVERT(CHAR(8), fecharegistro, 112) AS INT) BETWEEN CAST(CONVERT(CHAR(8), @desde, 112) AS INT) 
                                    AND CAST(CONVERT(CHAR(8), @hasta, 112) AS INT)";

        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("@desde", desde);
        cmd.Parameters.AddWithValue("@hasta", hasta);

        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(dt);
    }

    return dt;
}

La lógica principal se encontrara en el método creado para realizar la consulta, allí se ha utilizado el BETWEEN para definir el filtro entre un rango, pero este tienen un problema requiere de un formato especial que justamente el CONVERT (con el código 112) nos proporciona.

Esta conversión lleva la fecha al formato yyyyMMdd, o sea si tenemos la fecha 10/06/2010, será formateada a 20100610, claramente un numero, pero primero deberá pasar por un CHAR para adecuar la conversión del formato de la fecha y luego si será convertido a un valor numérico.

 

Ejemplo de Código


El código publicado fue desarrollado con visual Studio 2008.

La base de datos utilizada es el Sql Server Express 2008.

En el proyecto encontraran una carpeta de nombre “Script” que contiene el archivo .sql que podrán usar en el “Sql Server Management Studio” para crear la estructura de la base de datos en caso de tener problemas con el archivo .mdf

En caso de usar la base de datos integrada al servicio de Sql Server, se deberá cambiar la cadena de conexión definida en el App.config

 

[C#] 
[VB.NET] 

95 comentarios:

  1. Hola Leandro, solo un pequeño apunte, no es necesario hacer el Open del objeto Connection si estamos utilizando un objeto DataAdapter.

    ResponderEliminar
  2. Hola Leandro,

    yo para evitar utilizar CONVERT en la consulta Sql, por si conlleva consumo de recursos, lo que hago es añadir un día y hora 00:00:00 al hasta y en la consulta hago < hasta.
    ¿Estoy en lo cierto?, ¿si la fecha fuera índice con el CONVERT se utilizaría?.

    Un saludo.

    ResponderEliminar
  3. hola Javier

    Tienes razon, el DataAdapter no requiere una apertura explicita de la conexion.

    Se ve que tome una parte del código en donde usaba un command simple y quedo esa linea.

    Gracias por la aclaracion.

    saludos

    ResponderEliminar
  4. hola ATP

    La verdad nunca aplique esa tecnica que comentas, por eso no sabria decir si es valida o no, pero si en tu caso devuelve los registros que requieres se podria decir que es correcta.

    Por el tema de consumo de recurso, puede ser que la conversion de tipos lo sea, pero tampoco deberia ser demasiado, no realice un test de performance con una carga importante de registros, pero tampoco deberia ser tanta la diferencia con alguna otra tecnica que se pudiera aplicar.

    Como poder seguro se puede, aunque quizas la indexacion por ese campo la pierdas, y toda la optimizacion que esto lleva.

    saludos

    ResponderEliminar
  5. Hola Leandro me parece muy util tu post que se acerca a lo que estoy realizando ahora, pregunto como harías para filtrar x el Mes de esta Fecha?

    ResponderEliminar
  6. hola Ron@ldo VM

    Para filtrar solo por el mes pdorias saur la funcion DATEPART

    con esta funcion podrias tomar solo el mes de la fecha que estas filtrando

    SELECT Id, Descripcion, fecharegistro, usuario
    FROM RegistroActividades
    WHERE DATEPART(month, fecharegistro) >= @mesdesde
    AND DATEPART(month, fecharegistro) <= @meshasta

    En este caso le pasarias como desde y hasta solo los meses que quieres visualizar.

    saludos

    ResponderEliminar
  7. tendras una forma de filtrar un rango de fechas a un crystal report desde como obtener las fechas hasta como recibirlas desde el crystal repor?

    ResponderEliminar
  8. hola omar

    No se si entendi del todo el planteo de la pregunta

    Si el reporte en Crystal lo usas con DataSet Tipados el filtro por rango de fechas lo aplcias durecto en la query que carga el dataset, y luego este ya filtrado en el rango lo asignas como origen de datos del reporte.

    No se si es este el planteo que hacias, o pensabas filtrar la fecha dentro del reporte, quizas porque este se conecta directo a la db.

    saludos

    ResponderEliminar
  9. Hola Leandro, quisiera consultarte como agrego una seleccion por datetimepiker como fecha inicial y fecha final en crystal report por el momento lo tengo asi.

    Uso Visual Studio 2008

    DSTareas dst = new DSTareas();
    DSTareasTableAdapters.TareasTableAdapter adaptador = new MS2.DSTareasTableAdapters.TareasTableAdapter();
    adaptador.Fill(dst.Tareas);
    CrystalReport2 Report = new CrystalReport2();
    Report.SetDataSource(dst);
    crystalReportViewer1.ReportSource = Report;

    ResponderEliminar
  10. Hola Leandro que tal no puedo ejecutar esas consultas cuando esta la bd .mdf siempre me vota un mensaje similar a este
    the database.......[CSHARP]FILTRARRANGOFECHAS\FILTRARRANGOFECHAS\BIN\DEBUG\LOGACTIVIDADES.MDF' cannot be opened because it is version 655. This server supports version 612 and earlier. A downgrade path is not supported.
    Could not open new database

    Y he probado con sql 2008 estandar edition , sql 2005 ,sql 2005 expresss, con cual podria ejecutar la aplicacion sin problemas.

    ResponderEliminar
  11. hola Sergio

    Lo que podrias hacer es definir un metodo en el TableAdapter para que permita especificar las fechas desde y hasta que usarias en la query.

    De esta forma cargarias el datatable directo con la info que coincide con el rango de fechas que quieres mostrar.

    Para crear un nuevo metodo, podrias guarte por este articulo:

    Building a DAL using Strongly Typed TableAdapters and DataTables in VS 2005 and ASP.NET 2.0


    Alli veras como se extiende la funcionalidad del TableAdapter, creando nuevas consultas que cargan el datatable, en tu caso crearias uno nuevo que tome dos parametros de fecha

    saludos

    ResponderEliminar
  12. hola Augusto

    Me parece que el problema se presenta porque las mdf del ejemplo las cree con Sql Server Express 2008 R2

    Eso del R2 es una version mayor y trae el problema.

    En todo caso veo el finde semana de dejar los script para que puedas crear al db en otra version.

    saludos

    ResponderEliminar
  13. Ok.
    Leandro esperare el script .
    Gracias.
    Saludos.
    Feliz Navidad!

    ResponderEliminar
  14. Hola Leandro estoy necesitando ayuda en lo siguiente: necesito q figure la ficha del dia en el crystal report, utilizo un procedure store y no deseo generar una tabla temporal para alojar los datos ya q eso hace q la consulta sea mas lenta, me gustaria saber como hago para q el crystal report me tome la fecha del sistema? espero puedas ayudarme y gracias!!

    ResponderEliminar
  15. hola paola

    Crystal dispone de Special Filed que podrias usar, si la idea es mostrar la fecha del dia en el reporte.

    Mira este otra consulta que se hace en el foro

    http://social.msdn.microsoft.com/Forums/es/vbes/thread/a0d72949-a38d-43d2-9e4e-9d61cd71e0a2

    checkea la imagen para orientarte, creo que sera de ayuda

    saludos

    ResponderEliminar
  16. hola muy bueno tu blog me ha servido de mucha ayuda, sabes que pasa que yo trabajo con acces 2010 y necesito hacer una query que contenga el between con las dos fechas, pero al aplicar lo que expones de las fechas en betwenn salta un error "Error de IErrorInfo.GetDescription con E_FAIL(0x80004005)" cabe recalcar que las dos fechas vienen en formato dd/mm/yyyy y entran con parametros de entrada (tipo string) en el metodo que creé

    ResponderEliminar
  17. hola cachabacha

    si es logico que no funcione porque esta tecncia que menciono es particular para sql server

    quizas en access se puede aplciar alguna mas simple

    a ver veamos, cuando insertas los registros en la tabla de access, usas parametro ? imagino que si
    entonces le pasa la fecha como ser

    DateTime.Now.Date
    o
    DateTimePicker1.Value.Date

    ese Date que pongo es importante porque pasara al campo un valor sin la compojnente de la fecha

    si esto lo haces asi entocnes podrias poner un filtro de esta forma

    cmd.Parameters.AddWithValue("@fechadesde", DatetimePicker.Value.ToString(yyyyMMdd))

    para el hasta seria igual

    con esto aplicas un filtro en formato estandar en la fecha

    valida si con este tips que menciono funcion

    saludos

    ResponderEliminar
  18. Eres el mejor leandro muchisisisisimas gracias me funciono a la perfeccion :DDD

    por ultimo ( por ahora) me gustaria saber:

    como puedo hacer que desde un boton yo pueda modificar los datos de un datagriview en ejecucion es decir que modifique los datos y que cuando pulse el boton me diga que se modificaron o si hay otra manera mejor que me puedas decir

    que sea en c sharp por favor

    ResponderEliminar
  19. hola Joel

    bien para esto hay varias formas de lograrlos

    podrias hacer algo como esto

    Cómo: Enlazar datos al control DataGridView de formularios Windows Forms

    en donde el CommandBuilder creas las actualziaciones por ti y el Update() de DataAdapter impacta los cambios

    O podrias controlar tu toda la operacion, recorres los registros armas entidades y actualizas

    [N-Tier] – Desarrollo en capas - Ejemplo Facturacion - parte 2

    depende de que tanto te animes a desarrollar opta por una u otra

    saludos

    ResponderEliminar
  20. Saludos Leandro.
    Tengo Problemas al almacenar la fecha en una base de datos sql.
    insert into tabla (fecha) values ('18/11/2011') <-- En este ejemplo la almacena correctamente, pero si lo hago de la sig. manera.
    string Fecha = DateTime.Today.ToShortDateString();
    insert into tabla (fecha) values ('"+Fecha+"') <-- en este ejemplo me marca un error que dice Incorrect syntax near '18/11/2011'.
    Me podrias ayudar.

    ResponderEliminar
  21. hola Chipujin

    como primer punto debo mencionar que el principal problema se deba a que no usas parametros, no es bueno concatenar la query en un string
    debes usar parametros, para que al pasarle el datetime funcione

    ademas no has comentado si el campo en la db lo defines como datetiem o como varchar

    saludos

    ResponderEliminar
  22. Saludos Leandro.
    el campo en la base de datos es Datetime, lo que no entiendo es por que insertando directamente el valor en el campo si lo registre y por que al insertar el mismo valor pero ahora almacenándolo en una variable string que se convierte a Datetime no lo registre y marque ese tipo de error.

    ResponderEliminar
  23. hola

    mas alla que usando un string lo inserte y concatenado la cadena no lo haga deberias usar parametros es la forma mas segura de insertar informacion en una tabla

    saludos

    ResponderEliminar
  24. Ok, Gracias Leandro por tu ayuda, si se puede de la forma que dices usando parametros.
    Muchas Gracias!!!

    ResponderEliminar
  25. Hola Leandro,

    Disculpa pero quisiera convertir esta parte de la consulta sql a una consulta para bases de datos acces, es posible?

    Dim sql As String = "SELECT partida,concepto,precio,texto,total,fechapago " & _
    "FROM pagos " & _
    "WHERE CAST(CONVERT(CHAR(8), fechapago, 112) AS INT) " & _
    "BETWEEN CAST(CONVERT(CHAR(8), @desde, 112) AS INT) " & _
    "AND CAST(CONVERT(CHAR(8), @hasta, 112) AS INT)"

    ya que si lo dejo asi me genera este error: Error: IErrorInfo.GetDescription failed with E_FAIL(0x80004005)
    Podrias ayudarme?

    ResponderEliminar
  26. hola radx

    primero ese mensaje de error no creo que tenga que ver con con un problema de la query que adaptas a Access, parece mas un error de COM por el mensaje

    segundo, no creo que las funciones de conversion existan en Access, no estoy seguro que exista un adaptacion exacta

    saludos

    ResponderEliminar
  27. Hola Leandro,

    Entonces para hacer una consulta en access donde ponga como limite fechas como la haria? Ya he intentado hacerlo con los codigos que pones de ejemplo, pero no me arroja el resultado de la busqueda en el DataGridVew a que se debe esto? Necesito generar unreporte con estos datos.

    ResponderEliminar
  28. repondiendo a radx creo que te puedo ayudar, tambien tuve los mismos problemas al querer hacer el between en una consulta con Acces pero lo solucione de la siguiente manera:

    SELECT (lo que vas a consultar) y en la parte del between va así BETWEEN #" + fecha1 + "# AND #" + fecha2 + "#" donde fecha1 y fecha2 son variables de entrada tipo string pero convertidas previamente asi dateTimePicker1.Value.Date.ToString("MM/dd/yyyy")cualquier pregunta que tengas con gusto la responderé

    ResponderEliminar
  29. Gracias por contestar cachabacha,

    Ya intente hacer la solución que me propones y me quedo asi;

    Dim sql As String = "SELECT partida,concepto,precio,texto,total,fechapago " & _
    "FROM pagos " & _
    "WHERE fechapago= BETWEEN #" + DateTimePicker1.Value.Date.ToString("MM/dd/yyyy") + "# AND #" + DateTimePicker2.Value.Date.ToString("MM/dd/yyyy") + "#"

    El detalle es que todabia no me arroja los registros me da este error;

    Error de sintaxis (falta operador) en la expresión de consulta 'fechapago= BETWEEN #06/18/2012# AND #06/20/2012#'.

    Como podria solucionarlo??

    ResponderEliminar
  30. hola radx

    si usas el BETWEEN no debes poner el =

    solo es

    fechapago BETWEEN ...

    saludos

    ResponderEliminar
  31. leandro ya te ha dicho lo que tienes mal saludos.

    ResponderEliminar
  32. alguien me puede hacer el favor de ayudarme con el codigo de filtrar por 2 fechas que estan en una columna del datadridview
    estoy haciendolo en vb 2008 y access 2007 la verda e buscado y intentado mucho pero no me quiere dar
    es para mostrar en el datadridview unas ventas realizadas en el transcurso de esa fecha

    de antemano gracias

    la verdad ni se como empezar el codigo estoy en 0

    ResponderEliminar
  33. hola Leonel

    y tenias que usas Access, no podrias ir a una base de datos mejor como sql server

    es que en access las fechas son todo un tema

    igualmente si asignas como parametro un datetime deberias poder filtrar la query


    saludos

    ResponderEliminar
  34. haslo con 2 datetimepickers. a cada datetime agregale el evento ValueChanged, este evento se ejecuta cuando cambias el valor del datetimepicker, dentro del evento actualizas el datagridview con la fecha que envias de los datetimepickers. saludos

    ResponderEliminar
  35. Tienes algun ejemplo de como hacerlo en C# y Mysql

    ResponderEliminar
  36. hola McMoran

    la verdad no tengo un ejemplo con mysql, pero basicamente la tecnica es la misma, lo unico que cambia es la funcion de normalizacion para quitar la componente de la hora


    12.7. Date and Time Functions


    podrias usar el DATE() para tomar una parte de la fecha

    Nota: por supuesto las clases MySqlConnection, MySqlCommand , etc eso tambien cambia

    saludos

    ResponderEliminar
  37. Buenas tardes, estoy realizando un programa con una tabla de access 2003, estoy usando C# de Visual Studio 2005.

    Este procedimiento es un botón, que al pulsarlo me debe traer los registros de la tabla de VENTAS de acuerdo al rango de fechas que yo le digite, el problema es que ya no me trae nada. Las fechas son dos campos dateTimePicker1, y en el código ya le puse los parámetros para evitar problemas de que traiga la hora y no ponga problema de compatibilidad de campos por sus tipos.

    Antes de ponerle lo de las fechas, y probándolo que me trajera los registros de la tabla, sí me los traía todos; ahora que le di la condición de las fechas, y los parámetros ya no me muestra nada de nada, y sabiendo que determinadas fechas sí tienen información realmente en la base de datos.

    Soy prncipiante en esto, les agradezco mucho en lo que me puedan ayudar.

    private void BGENEREPOR_Click(object sender, EventArgs e)
    {
    dtscierrev.Clear();

    //string rquery = @"SELECT VENTAS.*, CODIGOS.NOMARTI, ARTICULOS.DESCRIP FROM ((VENTAS INNER JOIN ARTICULOS ON VENTAS.REFEREN = ARTICULOS.REFEREN) INNER JOIN CODIGOS ON VENTAS.CODIGO = CODIGOS.CODIGO) where FECHAVEN between @desde AND @hasta";

    string rquery = @"SELECT * FROM VENTAS WHERE FECHAVEN >= @desde AND FECHAVEN <= @hasta";

    DateTime fecha = DateTime.Today;
    string fechacadena = fecha.ToString("dd/MM/yyyy");

    DateTime fecha2 = DateTime.Today;
    string fechacadena2 = fecha2.ToString("dd/MM/yyyy");

    fechacadena = Convert.ToString(DFECHADES.Text);
    fechacadena2 = Convert.ToString(DFECHAHAS.Text);

    OleDbCommand cmd = new OleDbCommand(rquery, oleDbConnection1);

    cmd.Parameters.AddWithValue("@desde", fechacadena);
    cmd.Parameters.AddWithValue("@hasta", fechacadena2);

    OleDbDataAdapter da = new OleDbDataAdapter(cmd);

    da.Fill(dtscierrev);

    // Crea el reporte
    repventas repvent = new repventas();

    // Asocia el conjunto de datos con el reporte
    repvent.SetDataSource(dtscierrev);

    // Asocia el reporte con el Visualizador
    crysrepver2.ReportSource = repvent;
    }

    ResponderEliminar
  38. hola Juan

    es que deberia ser


    string rquery = @"SELECT * FROM VENTAS WHERE FECHAVEN >= @desde AND FECHAVEN <= @hasta";

    OleDbCommand cmd = new OleDbCommand(rquery, oleDbConnection1);

    cmd.Parameters.AddWithValue("@desde", Convert.ToDateTime(DFECHADES.Text));
    cmd.Parameters.AddWithValue("@hasta", Convert.ToDateTime(DFECHAHAS.Text));

    OleDbDataAdapter da = new OleDbDataAdapter(cmd);

    o sea los valores deben ser datetime
    saludos

    ResponderEliminar
  39. Gracias Leandro, ahora lo que pasa es que me está buscando con la hora, el campo ya está guardando la hora, por eso le había puesto fechacadena y fechacadena2. ¿qué será?

    ResponderEliminar
  40. hola Juan

    claro el tema es que si usas el BETWEEN deberias pasar por parametro la fecha en formato yyyyMMdd porque este solo reconoce un numero como parametro

    string fecha1 = Convert.ToDateTime(DFECHADES.Text).ToString("yyyyMMdd");

    cmd.Parameters.AddWithValue("@desde", fecha1);

    esto mismo esta explicado en el articulo

    saludos

    ResponderEliminar
  41. Buenas tardes Leandro, precisamente antes así lo habia hecho, con parámetros y teniendo en cuenta tu artículo,pero nada. Asi lo puse nuevamente prácticamente como lo tenia antes con la duda, pero aún no me trae nada, no se poorqué, porqué registros hay en la fechas que le doy pero en ninguna fecha trae nada de nada, gracias por tu ayuda y disculpa tanta molestadera:

    private void BGENEREPOR_Click(object sender, EventArgs e)
    {
    dtsciervent1.Clear();

    string fecha1 = Convert.ToDateTime(DFECHADES.Text).ToString("dd/MM/yyyy");
    string fecha2 = Convert.ToDateTime(DFECHAHAS.Text).ToString("dd/MM/yyyy");

    //string rquery = @"SELECT VENTAS.*, CODIGOS.NOMARTI, ARTICULOS.DESCRIP FROM ((VENTAS INNER JOIN ARTICULOS ON VENTAS.REFEREN = ARTICULOS.REFEREN) INNER JOIN CODIGOS ON VENTAS.CODIGO = CODIGOS.CODIGO) where FECHAVEN between @desde AND @hasta";

    string rquery = @"SELECT * FROM VENTAS WHERE FECHAVEN >= @desde AND FECHAVEN <= @hasta";

    OleDbCommand cmd = new OleDbCommand(rquery, oleDbConnection1);

    cmd.Parameters.AddWithValue("@desde", fecha1);
    cmd.Parameters.AddWithValue("@hasta", fecha2);

    OleDbDataAdapter da = new OleDbDataAdapter(cmd);

    da.Fill(dtsciervent1);

    // Crea el reporte
    repventas repvent = new repventas();

    // Asocia el conjunto de datos con el reporte
    repvent.SetDataSource(dtsciervent1);

    // Asocia el reporte con el Visualizador
    crysrepver2.ReportSource = repvent;
    }

    ResponderEliminar
  42. en el campo de la BD de access ese campo es fecha/hora, y abajo le puse que fuera corta, y si le quito los "/" slash que separa la fecha, me dice que son los campos incompatibles entonces por eso no se los quito en el campo cuando los converto...

    ResponderEliminar
  43. hola Juan

    el parametro no tiene que ser un string, sino un DateTime

    cmd.Parameters.AddWithValue("@desde", Convert.ToDateTime(DFECHADES.Text));
    cmd.Parameters.AddWithValue("@hasta", Convert.ToDateTime(DFECHAHAS.Text));


    saludos

    ResponderEliminar
  44. Puse lo que me sugeriste anteriormente que fué esto, que pena no entiendo. ¿qué pasa entonces con fecha1 y fecha2?:

    string fecha1 = Convert.ToDateTime(DFECHADES.Text).ToString("yyyyMMdd");

    cmd.Parameters.AddWithValue("@desde", fecha1);

    esto mismo esta explicado en el articulo

    ResponderEliminar
  45. Lo había puesto como me dijiste Datetime, luego te comenté que ahora me salía la hora en el mismo campo, me dijiste que para evitar esto usara los string yyyyMMdd, por eso los puse asi...

    ResponderEliminar
  46. hola Juan

    creo que estas confundiendo las tecnicas

    una cosa es usar el WHERE con el < o > y otro distinta es usar el BETWEEN

    para el WHERE con < o > necesitas asignar un datetime, para el between debe ser un numero, pero numero no un string conformato

    si usas el between deberia ser

    int fecha1 = Convert.Toint32(Convert.ToDateTime(DFECHADES.Text).ToString("yyyyMMdd"));

    saludos

    ResponderEliminar
  47. Juan

    me olvidaba, recuerda que al campo en la query debes aplicarle un CAST o CONVERT sino no funiona

    y por supuesto el campo debe ser datetime en la db


    saludos

    ResponderEliminar
  48. Listo muchas gracias de verdad, lo voy a poner con > <, sí estoy muy nobato y he estado autocapacitándome; lo voy a poner datetime, pero ahora me va a salir lo que te dije en el campo también va ha traer la hora, ahí qué? lo puedo asignar el string con formato cierto "dd/MM/yyyy" cierto? y me disculpas nuevamente...

    ResponderEliminar
  49. hola Juan

    pero tu necesitas filtrar por la hora tambien ?
    es cierto que el datetime de la db pondra la hora, pere podrias despreciar esta parte si al insertar el registro usas la propiedad "Date" del datetime para que envie una hora estandar y queden todos los campos igual

    no necesitas ningun string de la hora porque sino vas a filtrar por esta, con al tecnica del articulo al quitas del medio

    saludos

    ResponderEliminar
  50. Gracias Leandro, creo que no me has entendido, disculpa ponerte a voltear tanto con este tema. No necesito filtrar por hora, pero cuando pongo date time la pone; le corrijo eso con la técnica que nos enseñas acá, y no me trae nada, ese es el problema.

    El código es este, ¿donde puede estar el problema y lo ccrrijo... gracias.

    private void BGENEREPOR_Click(object sender, EventArgs e)
    {
    dtsciervent1.Clear();

    string fecha1 = Convert.ToDateTime(DFECHADES.Text).ToString("dd/MM/yyyy");
    string fecha2 = Convert.ToDateTime(DFECHAHAS.Text).ToString("dd/MM/yyyy");

    //string rquery = @"SELECT VENTAS.*, CODIGOS.NOMARTI, ARTICULOS.DESCRIP FROM ((VENTAS INNER JOIN ARTICULOS ON VENTAS.REFEREN = ARTICULOS.REFEREN) INNER JOIN CODIGOS ON VENTAS.CODIGO = CODIGOS.CODIGO) where FECHAVEN between @desde AND @hasta";

    string rquery = @"SELECT * FROM VENTAS WHERE FECHAVEN >= @desde AND FECHAVEN <= @hasta";

    OleDbCommand cmd = new OleDbCommand(rquery, oleDbConnection1);

    cmd.Parameters.AddWithValue("@desde", fecha1);
    cmd.Parameters.AddWithValue("@hasta", fecha2);

    OleDbDataAdapter da = new OleDbDataAdapter(cmd);

    da.Fill(dtsciervent1);

    // Crea el reporte
    repventas repvent = new repventas();

    // Asocia el conjunto de datos con el reporte
    repvent.SetDataSource(dtsciervent1);

    // Asocia el reporte con el Visualizador
    crysrepver2.ReportSource = repvent;
    }

    ResponderEliminar
  51. hola Juan

    el problema esta en que asignas string a un parametro que deberia ser datetime

    usa

    cmd.Parameters.AddWithValue("@desde", Convert.ToDateTime(DFECHADES.Text).Date);
    cmd.Parameters.AddWithValue("@hasta", Convert.ToDateTime(DFECHAHAS.Text).Date);

    saludos

    ResponderEliminar
  52. Buenas noches Leandro, vuelvo a lo mismo del principio, otra vez aparece con la hora el campo de la fecha y así no lo necesito, por eso le había puesto el string que antes me habías dicho.

    ResponderEliminar
  53. hola Juan

    partamos de una base, si el campo de tu tabla es datetime, con un string como quieres plantearlo no funciona, eso es seguro

    ahora bine dices que vuelve con la hroa, lo cual es es cierto el "Date" del Datetime si devuelve la hora, pero es una hora normalizada a las 12:00:00, si usando el CONVERT en la query eliminas la hora esta tambien se normaliza, por lo tanto se anula la hora como filtro porque no afectara

    de un datetime la hora nunca podras quitarla, pero si puede anular el efecto al filtrar

    saludos

    ResponderEliminar
  54. Gracias Leandro, pero ¿cómo se hace en el query? eso varia de un lenguaje a otro y no se como hacerlo. Agradezco mucho tu colaboración.

    ResponderEliminar
  55. hola Juan

    en la query se aplica como se plantea en el articulo

    cuando te refierea a lenguaje apuntas a .net ? porque usar el CONVERT es en sql, .net no tiene nada que ver

    si varia de una db a otra, si usas por ejemplo MySql, Oracle, Access, etc ahi si varia la forma en como usar la funcion de conversion

    saludos

    ResponderEliminar
  56. exacto, uso Access ahí tengo el problema.

    ResponderEliminar
  57. hola

    quizas se pueda armar algo con el

    DatePart

    podrias usar la funcionalidad del between para concatener el año-mes-dia tomando cada parte

    saludos

    ResponderEliminar
  58. Hola Leandro.

    Tengo una pregunta, me podrías explicar como hacer una consulta entre dos fechas pero con el asistente de Querys (consultas) ya que yo trabajo con el DataSet Designer

    Gracias

    ResponderEliminar
  59. hola x-carlos

    como se plantea aqui

    Tutorial 1: Creating a Data Access Layer

    puedes crear metodos custom en los tableadapters asociados a los datatable del dataset tipado

    saludos

    ResponderEliminar
  60. Hola Leandro, me gusta mucho tu Blogg, es muy completo. Te queria consultar, necesito filtrar en un gridview la fecha

    Uso vb.net y para queries SQL server 2008

    Te muestro como tengo la query

    Es un stored procedure

    @lDate as date,

    AS
    DECLARE @sTSQL as nvarchar(1500)

    SET @sTSQL = @sTSQL + 'CONVERT(VARCHAR(10), pe.peDate,103) AS DATE,'


    SET @sTSQL = @sTSQL + ' AND (peDate = ''' + CONVERT(varchar(10), @lDate , 103) + ''')'


    El tema es asi quiero filtrar una serie de eventos por fecha, en un textbox (lo que ingreso es un string, 01/01/2013). Pero quiero que a penas entro al programa me muestre TODOS los eventos para poder ir filtrando y que me traiga el dato que contenga la fecha que yo le paso.


    Espero se haya entendido.

    Gracias saludos y segui asi ;)

    ResponderEliminar
  61. hola Magali

    en el procedure, donde define el WHERE?
    lo pregunto porque en esta linea

    SET @sTSQL = @sTSQL + 'CONVERT(VARCHAR(10), pe.peDate,103) AS DATE,'

    veo que realizas una conversion de la fecha, pero no comparas contra ningun campo

    la conversion de tipos parece esta correcta, imagino desde codigo .net estas asignado al parametro de la coleccion del SqlCommand, un valor del tipo DateTime

    saludos

    ResponderEliminar
  62. Ya lo resolvi Leandro.

    Muchas gracias, hasta la proxima.

    ResponderEliminar
  63. Hola Leandro, me intereso mucho tu Blogg, esta muy completo. Te queria consultar, necesito filtrar en un gridview la fecha

    Uso vb.net y acces 2010 como puedo hacerlo kiero jacer lo mismo k hiciste pero en este caso en acces

    ResponderEliminar
  64. Hola Leandro, me intereso mucho tu Blogg, esta muy completo. Te queria consultar, necesito filtrar en un gridview la fecha

    Uso vb.net y acces 2010 como puedo hacerlo kiero jacer lo mismo k hiciste pero en este caso en acces

    ResponderEliminar
  65. hola jose

    pero esto mismo puedes aplicarlo con Access sin problemas, es mas puedes asignar DateTime por parametro a la query

    lo que si valida si en la db access se registra la hora o no, en caso de estar la hora quizas podrias ver de aplicar formato y ponerlo en yyyyMMdd para usar el BETWEEN

    si usas las clases de OleDbConection, OleDbCommand podrias aplicar esto mismo que uso en el articulo

    saludos

    ResponderEliminar
  66. Buen Día Leandro gracias por contestar oye ya lo hice y si me funciono solo que tiene un detalle no me muestra las fechas mi codigo que tengo es este:

    Private Sub filtrar_Click(sender As Object, e As EventArgs) Handles filtrar.Click
    Try
    Using cnn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" _
    & "C:\Carpeta\Mi_base.accdb" & ";Persist Security Info=False;")
    Dim cmd As OleDbCommand = cnn.CreateCommand()
    cnn.Open()
    'Construimos la consulta SQL de selección con dos parámetros. '
    cmd.CommandText = _
    "SELECT Expedida, validez FROM Sujeto WHERE Expedida >=@DESDE AND Validez <=HASTA"
    '"WHERE CampoFecha BETWEEN @desde AND @hasta"

    'Añadimos los parámetros, utilizando solamente
    'el componente de la fecha, sin la hora.


    cmd.Parameters.AddWithValue("@desde", DESDE)
    cmd.Parameters.AddWithValue("@hasta", HASTA)

    Dim da As New OleDbDataAdapter(cmd)
    Dim dt As New DataTable()
    da.Fill(dt)
    ' Mostramos los datos en un control DataGridView.
    DGV.DataSource = dt
    End Using
    ''''''''''''''''''''''''''''''''''''''''''''
    '''''''''''''''''''''''''''''''''''''''''''
    Return

    Catch ex As Exception
    MessageBox.Show(ex.Message)

    End Try

    End Sub

    ResponderEliminar
  67. hola jose

    las variables DESDE y HASTA son variables del tipo DateTime?
    porque si lo son quizas debas usar

    cmd.Parameters.AddWithValue("@desde", DESDE.Date)

    para que solo envie la fecha sin la hora, eso si asegurate que en la tabla no se defien un horario en la fecha

    saludos

    ResponderEliminar
  68. Hola Leandro una pregunta como le puedo hacer para filtrar dos fechas en visual y access con datagridview.. lo que pasa que quiero que al definir un rango de fechas este me devuelva en el datagridview las licencias que van a vencer en ese rango, mi sistema consiste en avisar que licencias de conducir van a vencer dentro del rango que definamos..

    ResponderEliminar
  69. hola jose

    pero esto lo respondi en la respuesta anterior

    puedes aplicar esta misma tecnica si es que en access no guardas la componente de la hora, si solo dejas fechas puedes asignar el parametro para filtrar

    saludos

    ResponderEliminar
  70. Hola Leandro muy bueno el blog, tengo una consulta yo estoy usando un datetimepicker para el ingreso de datos de la fecha para el usuario en el sistema que estoy desarrollando para mi tesis, lo que necesito es que como valor maximo de la fecha permitida para ingresar figure 12 años para atras desde el momento en que se está ingresando los datos, es para que solo admita registrar a personas mayores de 12 años, gracias

    ResponderEliminar
  71. hola Samuel

    DateTimePicker.MaxDate Property

    podrias definir la fecha maxima que vas a permitir al usuario seleccionar

    a la fecha que tienes le sumas con el AddYears()
    saludos

    ResponderEliminar
  72. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  73. Eres grande Leandro, tus ejemplos me han servido muchisimo de verdad que todo esto me ah facilitado la vida como no tienes idea, es el ejemplo mejor explicado y más sencillo de todos los que eh visto por internet, no dejes de subir tutos! saludos

    ResponderEliminar
  74. Buenos dias Leandro.
    Te hago una consulta,estoy realizando una consulta con fechas usando he usando operadores >= <= , también usé BETWEEN.

    elcampo fecha tiene tipo de dato datetime en la tabla, cuando ejecuto el procedimiento almacenado desde este rango de fechas me arroja una cifra where fecha >=2014-02-01 and fecha <=2014-06-30 = 1687,pero cuando lo hago mes a mes de febrero a junio de 2014, al realizar la sumatoria por mes la sumatoria da 1946, cabe resaltar que estoy haciendo un distinct O GROUP BY al campo documento de identidad para que solo muestre los valores no repetidos.
    este es el procedimiento:

    [dbo].[spbuscarReporte]

    @DateFrom datetime,
    @dateTo datetime
    as
    SELECT DISTINCT Documento from [Historia_Medicina] WHERE Fecha >= @DateFrom AND Fecha <= @dateTo

    ResponderEliminar
  75. hola Orlando

    no validaste si la hora del campo datetime no deja registros por fuera

    en este mismo articulo lo menciono, por eso es que aplico un CONVERT para normalizar la hora

    saludos

    ResponderEliminar
  76. Hola
    tengo este problema al utilizar fechas , hago la usqueda de dos fechas con dos datetimepicker, la base de datos la tengo en access y eltipo de fecha en haces la tengo como text , el prolema que tengo esque cuando es la misma fecha si me muestra la fecha correcta pero ya cuando pones diferentes fechas ya no ,me dafechas de mas
    este es el codigo que tengo

    cmd.CommandText = "SELECT * FROM Tabla WHERE Fecha BETWEEN'" + dateTimePicker1.Text + "' AND '" + dateTimePicker2.Text + "'";

    que eslo que estoy haciendo mal ?

    ResponderEliminar
  77. hola BLITZ

    si analizaste este articulo veras que en ningun momento asigno los valores del select uniendolos a un string

    debes usar parametros para asignar el datetime, se usa el dateTimePicker1.Value al Parameters del SqlCommand

    lo que haces mal es no usar parametros

    saludos

    ResponderEliminar
  78. tengo esta consulta







    y tambien dos cajas te texto que serian mi rango de fechas y no me muestra nada, ojala que puedas ayudarme

    ResponderEliminar
  79. Me has salvado la vida con este post Leandro! Gracias! :D

    ResponderEliminar
  80. Buenas tardes Leandro, tengo un gran inconveniente y se que me puedes ayudar.
    Quiero filtrar por fechas pero fíjate como tengo mi código:
    protected void Button1_Click(object sender, EventArgs e)
    {
    emptacaDataSetE.entradaalDataTable dt = new emptacaDataSetE.entradaalDataTable();
    emptacaDataSetETableAdapters.entradaalTableAdapter da = new emptacaDataSetETableAdapters.entradaalTableAdapter();
    DateTime fecha1 = Convert.ToDateTime(Calinicio.SelectedDate.ToShortDateString());
    DateTime fecha2=Convert.ToDateTime(Calfinal.SelectedDate.ToShortDateString());

    da.FillBynotaing(dt,???????); He aquí mi problema, como hago para que me tome las fechas....

    ReportDataSource RD = new ReportDataSource();
    RD.Value = dt;
    RD.Name = "DataSet1";
    ReportViewer1.LocalReport.DataSources.Clear();
    ReportViewer1.LocalReport.DataSources.Add(RD);
    ReportViewer1.LocalReport.Refresh();
    }
    Gracias...

    ResponderEliminar
    Respuestas
    1. hola
      Un primer punto que podria mencionar es que al usar el SelectedDate este devuelve un datetime, si quieras quitarle la componente de la hora hubieras usado Calinicio.SelectedDate.Today

      Para que puedas pasar los valores deberias adaptar el FillBynotaing() del dataset tipado, pero me pregunto, sabes como extender un xsd ?
      Tutorial 1: Creating a Data Access Layer
      alli explica como poder crear metodos con parametros
      saludos

      Eliminar
  81. ok hermano lo voy a revisar. gracias.. saludos...

    ResponderEliminar
  82. Leandro: tengo una consulta desde vb 2010 a acces con fecha pero me invierte las fechas. Se que acces trabaja con mm-dd-aaaa pero mi fecha viene de un textbox osea es una cadena (string) . Este es mi codigo

    Function Filtro(ByVal busqueda As String, ByVal campo As String) As DataTable
    con.Open()
    Dim dt As New DataTable
    Dim da As New OleDbDataAdapter("SELECT * FROM Ficha WHERE [Fecha de Nacimiento] LIKE #" & busqueda & "#", con)
    da.Fill(dt)

    con.Close()
    Return dt

    End Function

    Desde ya muchas gracias por tu tiempo.

    ResponderEliminar
    Respuestas
    1. hola
      Deberias usar parametros en el codigo

      Function Filtro(ByVal busqueda As String, ByVal campo As String) As DataTable

      Dim dt As New DataTable
      Dim query As String = "SELECT * FROM Ficha WHERE [Fecha de Nacimiento] = @fecha"
      Dim cmd As New OleDbCommand(query, con)
      cmd.Parameters.AddWithValue("@fecha", Convert.ToDateTime(busqueda))

      Dim da As New OleDbDataAdapter(cmd)
      da.Fill(dt)

      Return dt

      End Function

      saludos

      Eliminar
  83. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  84. yo estoy buscando como hacer que un datepicker me de cierto rango de fechas que no me deje seleccionar fechas pasadas solo quiero seleccionar fechas de mañana en adelante y no encuentro como, estoy haciendo esto en windows form solo encuentro como hacerlo en wpf

    ResponderEliminar
  85. hola leandro
    tengo una bdd en access, con un campo de fecha, necesito filtrar por año, intente como lo explicaste con datepart, pero me arroja un error de System.Data.OleDb.OleDbException: 'No value given for one or more required parameters.', el nombre del campo ya lo verifique bien, el valor lo tomo de un textbox donde el usuario escribe e año que quiere, intente convertir el valor del textbox a entero pero me marca el mismo error, el string que uso es:
    convertido en entero
    int anio = Convert.ToInt32(tbanio0.Text);
    string cadena = "select * from facturas_vta where DatePart(yyyy,fecha_emision)= "+ anio +"";
    espero me puedas ayudar
    gracias

    ResponderEliminar
  86. Hola leandro me podrias ayudar por favor trato de hacer esto para MVC asp.net pero nose como quedaria mi vista

    ResponderEliminar
  87. deseo que traiga la info de nombre raza desde hasta pero con un solo dato que seria fecha registro, me urge gracias

    ResponderEliminar
  88. Buenas tardes como se hace en modelo vista contralador lo hice asi pero no me muestra el resultado que yo quiero:
    public ActionResult Cadena(string fromDate, string toDate)
    {
    var report = this.GenerateCadenaReport(fromDate, toDate);
    var stream = report.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat);
    return File(stream, "application/pdf");
    }

    private ReportClass GenerateCadenaReport(string fromDate, string toDate)
    {
    //string fromDate = TextBox1.Text;

    var connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
    var connection = new SqlConnection(connectionString);
    var dataTable = new DataTable();
    var sql = @"SELECT Estates.EstateId AS IdE, Barns.BarnId AS Id, Nutritions.DateOfBirth AS Fecha, Barns.Name AS Nave, Estates.Name AS Granja,
    Nutritions.Jumbo, Nutritions.AAA, Nutritions.AA, Nutritions.A, Nutritions.B, Nutritions.C, Nutritions.Blancos, Nutritions.Pipo,
    Nutritions.Rotos, SUM(Nutritions.Jumbo+Nutritions.AAA+Nutritions.AA+Nutritions.A+Nutritions.B+Nutritions.C+Nutritions.Blancos+
    Nutritions.Pipo+Nutritions.Rotos) AS Totales
    FROM Barns INNER JOIN
    Nutritions ON Barns.BarnId = Nutritions.BarnId INNER JOIN
    Estates ON Barns.EstateId = Estates.EstateId AND Nutritions.EstateId = Estates.EstateId
    WHERE CONVERT(DATETIME, Nutritions.DateOfBirth) >= @fromDate AND CONVERT(DATETIME, Nutritions.DateOfBirth) <= @toDate GROUP BY Estates.EstateId, " +
    "Barns.BarnId, Nutritions.DateOfBirth, Barns.Name,Estates.Name, Nutritions.Jumbo,Nutritions.AAA, Nutritions.AA, Nutritions.A, Nutritions.B,Nutritions.C," +
    "Nutritions.Blancos,Nutritions.Pipo,Nutritions.Rotos";


    try
    {
    connection.Open();
    var command = new SqlCommand(sql, connection);
    command.Parameters.AddWithValue("@fromDate", fromDate);
    command.Parameters.AddWithValue("@toDate", toDate);
    var adapter = new SqlDataAdapter(command);
    adapter.Fill(dataTable);
    }
    catch (Exception ex)
    {
    ex.ToString();
    HttpContext.Trace.Warn("ERROR: " + ex.ToString());
    }
    var report = new ReportClass();
    report.FileName = Server.MapPath("/Reportes/Cadena.rpt");
    report.Load();
    report.SetDataSource(dataTable);
    return report;
    }

    ResponderEliminar
  89. Hola Leandro! Aprendí muchisimo gracias a tus Blogs y respuestas en todas las web sobre vb.net... Lo que necesito hacer y no encuentro es lo siguiente: Tengo un datagrid que filtro por un rango de fechas y me funciona perfecto, en el mismo Form tengo un textbox que me filtra por varias columnas y tambien me funciona perfecto!... Pero como hago para que me filtre por el textbox y el rango de fecha todo junto?
    Saludos!

    ResponderEliminar