none
Cómo usar parámetro que puede ser nulo o no en una consulta RRS feed

  • Pregunta

  • Hola,

    quiero hacer con linq esto que se suele hacer en sql:

    --Variable que puede ser nulo o no
    declare @mivar datetime;
    set @mivar = null;
    
    --aquí un filtro en el where de una consulta
    where (CampoTabla >= @mivar OR @mivar IS NULL)
    
    

    Es decir, que si mi variable está informada la utilice para filtrar el resultado y si viene a nulos me traiga los campos que están a nulos.

    Tengo un Entity Type obtenido con database first y quiero hacer una consulta que haga lo del sql.

    Es posible esto?

    martes, 30 de diciembre de 2014 16:01

Respuestas

  • public IQueryable<MyEntity> GetQuery(param1Type param1, param2Type param2)
    {
       IQueryable<MyEntity> q = contexto.LaTabla;
       if (param1 != null)
       {
          q = q.Where( x => x.campo >= param1);
       }
       if (param2 != null)
       {
          q = q.Where( x => x.campo <= param2);
       }
       return q;
    }

    Como ves, se puede hacer con uno o varios parámetros opcionales de búsqueda, sin tener que pedirle al proveedor LINQ que haga cosas raras.



    Jesús López


    EntityLite a lightweight, database first, micro orm



    • Editado Jesús López jueves, 8 de enero de 2015 20:24 x
    • Marcado como respuesta weatherby lunes, 12 de enero de 2015 9:53
    jueves, 8 de enero de 2015 18:32

Todas las respuestas

  • Con linq to Entities se hace de forma muy parecida a como lo harias con SQL:

    var q = from v in contexto.LaTabla
            where v.campo >= parametro || parmetro == null
            select v;

    • Propuesto como respuesta Willams Morales martes, 30 de diciembre de 2014 16:12
    • Votado como útil weatherby miércoles, 31 de diciembre de 2014 13:54
    martes, 30 de diciembre de 2014 16:05
  • Hola,

    Alberto, probando como me dices no funciona, no me devuelve ningún resultado.

    PD.- Feliz 2015 para tod@s

    miércoles, 31 de diciembre de 2014 13:54
  • intenta definiendo parentesis

     where ((v.campo >= parametro) || (parmetro == null))

    eso a veces influye en como aplica el filtro


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    miércoles, 31 de diciembre de 2014 14:14
  • Es decir, que si mi variable está informada la utilice para filtrar el resultado y si viene a nulos me traiga los campos que están a nulos.

    Esto que usted pone no concuerda con el T-SQL.

    El significado del WHERE del T-SQL:  donde CampoTabla es mayor o igual a @mivar o bien, que @mivar sea nulo.  Esto implica que un SELECT que usa ese WHERE devolverá toda la tabla cuando @mivar sea nulo.  Esto no es lo que usted dice en la cita que hice más arriba.

    En la cita textual, usted dice "que me traiga los campos que están a nulos".  El WHERE que hace lo que la cita dice, según mi entendimiento, sería:

    WHERE (CampoTabla >= @mivar) OR (@mivar Is Null AND CampoTabla Is Null)

    Este WHERE que le muestro devolverá únicamente aquellos registros que tengan NULL en CampoTabla pero solamente cuando @mivar también es NULL.


    Jose R. MCP
    Code Samples


    • Editado webJose miércoles, 31 de diciembre de 2014 14:17
    miércoles, 31 de diciembre de 2014 14:15
  • Hola,

    feliz año y disculpas por la tardanza en responder, pero estas son fechas complicadas.

    webJose, tienes razón, creo que no me expliqué bien, pero básicamente lo que quiero es que si @mivar viene informada me aplique el filtro y si viene a nulo no aplique ningún filtro sobre el campo.

    Volviendo al tema LINQ, he probado lo que me indican Alberto y Leandro y solo funciona parcialmente. Si el parámetro (o variable de filtro) como se quiera llamar, está a null sí que recupera todos los registros, pero cuando informo un valor de fecha no me devuelve nada.

    Intento poner un ejemplo, tengo 10 registros y tienen un campo FechaBaja, este campo está informado en un registro con el valor 01/01/2015, otro registro tiene en este campo el valor 06/01/2015 y el resto de registros están a null.

    Si hago la consulta con un valor de 05/01/2015 en la variable de filtro me debería devolver 9 registros y actualmente no me devuelve ninguno.

    jueves, 8 de enero de 2015 8:02
  • >>pero cuando informo un valor de fecha no me devuelve nada.

    estas seguro que la componente de la hora no estara afectando en el filtro? no habias comentado que el parametro era una fecha

    valida si en la db la fecha la guardar tambien con la hora

    si es asi no puedes usar las SqlFunctions, usando el DatePart o DateDiff para trabajar las fecha

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    jueves, 8 de enero de 2015 14:13
  • Hola Leandro,

    aunque en el post he puesto un día de diferencia en el conjunto de registros que manejo de pruebas hay diferencias de más de un mes.

    Entiendo que aun teniendo en cuenta la hora el filtro debería funcionar no?

    Yo he conseguido obtener el conjunto de registros haciendo esto:

    where ((v.campo >= parametro) || (v.campo == null))

    Pero no es exactamente lo que se pretende conseguir, que es que en el caso de que la variable de filtro venga a nulos el filtro simplemente no se aplique ya que de esta manera si un registro tiene un valor de fecha en v.campo la consulta no lo devolverá, mientras que en sql directamente sí que lo hace.

    Es por eso la pregunta original..., ¿en linq se puede hacer esto?

    jueves, 8 de enero de 2015 15:28
  • weatherby,

    Cuesta un poco entender lo que finalmente requieres. En resumen lo que requieres - espero no equivocarme - es que si el parámetro llega Null que haga caso omiso al parámetro, esto en T-SQL se consigue así

    WHERE
      (Campo = IsNull(@Parametro, Campo))

    Si te das cuenta en caso llegue null evaluará por si mismo y siempre dará true, caso contrario evaluará por el parámetro enviado.

    En Linq, no lo he probado pero comentanos si funciona

    where v.campo >= (parametro ?? v.campo)

    Lo que hago es buscar un sustituto a la función IsNull de t-sql

    Coméntanos si es lo que buscas y como te fue.

    Si la solución propuesta atendió su consulta no olvide marcarla como respuesta.


    Willams Morales P.
    Arequipa - Perú


    jueves, 8 de enero de 2015 16:00
  • Williams, debo aclarar algo de su respuesta:  Si bien es cierto que tal vez el operador ?? sirva en C#, el equivalente no es IsNull cuando ANSI NULLS está activo.  Cuando tenemos el comportamiento de ANSI NULLS en base de datos, un NULL nunca es igual a otro NULL.  Por lo tanto su uso de IsNull() es últimamente incorrecto y no equivalente.

    El SQL correcto sería WHERE Campo = @parametro OR @parametro Is Null.


    Jose R. MCP
    Code Samples

    jueves, 8 de enero de 2015 16:07
  • Pienso que las consultas LINQ que se han dado aquí parecen correctas, así que tal vez es un problema con los nulos.  Lo mejor en este punto sería iniciar SQL Profiler para interceptar el SQL generado por LINQ para determinar si es correcto o no.

    De hecho, pienso que hay poca posibilidad de que el SQL generado no sea correcto pues el lado derecho no incluye ningún campo de base de datos.  Es concebible y de hecho probable que LINQ ignore esta parte completamente a la hora de generar el T-SQL.  Con el SQL Profiler nos daremos cuenta.


    Jose R. MCP
    Code Samples


    • Editado webJose jueves, 8 de enero de 2015 16:11
    jueves, 8 de enero de 2015 16:09
  • José, gracias por la observación, siempre es bueno leer sus comentarios.

    Bueno, es que se dice mucho en este post y descuide leer todo, en la respuesta que le hace wheater a usted le menciona

    "...webJose, tienes razón, creo que no me expliqué bien, pero básicamente lo que quiero es que si @mivar viene informada me aplique el filtro y si viene a nulo no aplique ningún filtro sobre el campo."

    Basado en ello es que di mi propuesta, ahora que reviso a detalle observo que el campo de la tabla también puede contener valores Null por tanto es correcto lo que usted menciona. Yo sobre ello corregiría lo siguiente:

    WHERE
      (IsNull(Campo, 0) = IsNull(@Parametro, IsNull(Campo, 0)))

    jueves, 8 de enero de 2015 17:17
  • public IQueryable<MyEntity> GetQuery(param1Type param1, param2Type param2)
    {
       IQueryable<MyEntity> q = contexto.LaTabla;
       if (param1 != null)
       {
          q = q.Where( x => x.campo >= param1);
       }
       if (param2 != null)
       {
          q = q.Where( x => x.campo <= param2);
       }
       return q;
    }

    Como ves, se puede hacer con uno o varios parámetros opcionales de búsqueda, sin tener que pedirle al proveedor LINQ que haga cosas raras.



    Jesús López


    EntityLite a lightweight, database first, micro orm



    • Editado Jesús López jueves, 8 de enero de 2015 20:24 x
    • Marcado como respuesta weatherby lunes, 12 de enero de 2015 9:53
    jueves, 8 de enero de 2015 18:32
  • Con EntityLite es muy similar, ¡Pero mucho más eficiente!

    public partial class MyEntityRepository
    {
    
        public IQueryLite<MyEntity> SearchQuery(int? param1, string param2)
        {
            var q = this.Query(Projection.BaseTable);
            if (parm1.HasValue) 
            {
                q.Where(MyEntityFields.MyField1, OperatorLite.Equals, param1);
    
            }
            if (param2 != null)
            {
                q.Where(MyEntityFields.MyField2, OperatorLite.Equals, param2);
            }
            return q;
        }
    }



    Jesús López


    EntityLite a lightweight, database first, micro orm



    jueves, 8 de enero de 2015 18:40
  • por cierto esa sentencia sql debería se así, por razones de eficiencia:

    SELECT ...
    FROM ....
    WHERE
       Campo >= @mivar OR @mivar IS NULL
    OPTION (RECOMPILE)


    Si no pones el OPTION (RECOMPILE) el plan de ejecución que te sale es desastroso y encima se reutiliza.

    Si te pones a hacer cosas de estas en LINQ

    where ((v.campo >= parametro) || (v.campo == null))

    Te va a salir un plan de ejecución horrible, y además como estás con Entity Framewok no podrás decirle que use OPTION (RECOMPILE).

    Por cierto, con EntityLite sí que puedes poner opciones de query como OPTION(RECOMPILE) y otras opciones.



    Jesús López


    EntityLite a lightweight, database first, micro orm

    jueves, 8 de enero de 2015 18:47
  • Hola,

    al final lo haré como indica Jesús.

    Por cierto Jesús, gracias por la recomendación de utilizar OPTION(RECOMPILE), desconocía esta opción y las consultas donde aplico este tipo de filtrados he podido comprobar que me van más rápidas.

    Gracias a todos por la ayuda y el interés.

    lunes, 12 de enero de 2015 9:54