none
Manejo especial de WHERE RRS feed

  • Pregunta

  • Cordial saludo.

    Actualmente, utilizo el management studio para presentarle informes a mi jefe. En una de nuestras reuniones el quería ver los datos con algunos filtros, pero luego quiso ver todos los datos sin filtro alguno.

    En ese momento me di cuenta que para hacer los filtros necesitaba un WHERE, pero cuando mi jefe quería ver todos los datos, debí de quitar el WHERE.

    Un día conversando con un amigo que administra bases de datos en SQL SERVER, me dijo que habían unos caracteres espéciales que permitían dentro del WHERE, ver todos los datos.

    Me dijo que si el WHERE lo estaba haciendo para un campo tipo VARCHAR, colocara un espacio vacío (' ') y que esto automáticamente presentaba todos los datos que habían. De igual forma, me dijo que si el campo al que le iba a ser el WHERE era numérico, colocara cero (0) y eso permitiría que se vieran todos los datos, pero al hacer lo que me dijo, no me funciono.

    para ser mas claro voy a poner un ejemplo:

    Digamos que tengo la tabla siguiente:

    mes tipo galones
    1               AUTOMOVIL 300
    2               AUTOBUS 1000

    Estoy es un caso hipotético y busco que sea lo mas simple posible para darme a entender.

    En la Reunión con mi jefe le presento la siguiente consulta:

    SELECT mes

               ,tipo

               ,galones

    WHERE mes=2 AND tipo='AUTOBUS'

    Luego mi jefe me dice: "quiero ver los galones de los automóviles en el mes 1"

    Yo en el WHERE, simplemente hago los cambios así: WHERE mes=1 AND tipo="AUTOMOVIL"

    Lugo mi jefe me dice, "quiero ver los galones de todos los tipos y de todos los meses"

    En ese momento me toca quitarle a la consulta el WHERE.

    Ustedes dirán: "¿pero para que necesita hacer esto con el WHERE, si simplemente al quitarlo obtiene lo que le pide su jefe?"

    Lo hago así, porque estoy diseñando un sitio Web donde a la consulta deseo que el cliente, tenga la opción de hacer filtros o ver toda la información de acuerdo a su selección en un campo dispuesto para esto dentro del sitio web.

    Por favor tengan muy presente que lo único que deseo saber es si eso que quiero es posible hacerlo con el WHERE.

    Gracias.

     


    Germanq

    lunes, 19 de octubre de 2020 12:08

Respuestas

  • Hola German:

    ALTER Function dbo.fntbgalones
    				(
    				@Mes As  Int         
    			  , @Tipo As VarChar(50) 
    				)
    Returns Table
    As
    	Return
    	(
    	  Select mes
    		   , tipo
    		   , galones
    			 From dbo.autos
    			 Where 
    				 (@Mes = mes Or @Mes Is Null) 
    			 And (@Tipo = tipo Or @Tipo Is Null)
    	);
    GO

    Te has olvidado de la tabla.

    SELECT * FROM dbo.fntbgalones(null, null) fn

    Otra opción.

    ALTER Function dbo.fntbgalones
    				(
    				@Mes As  Int = null        
    			  , @Tipo As VarChar(50) = null
    				)
    Returns Table
    As
    	Return
    	(
    	  Select mes
    		   , tipo
    		   , galones
    			 From dbo.autos
    			 Where 
    				 (@Mes = mes Or @Mes Is Null) 
    			 And (@Tipo = tipo Or @Tipo Is Null)
    	);
    GO

    Usar default

    SELECT * FROM dbo.fntbgalones(default, default) fn
    A una función hay que pasarle los parámetros que espera. No así a un procedure, donde si estos se definen con un valor por defecto, no es obligatorio suministrarlos.

    lunes, 19 de octubre de 2020 13:36
  • Hola, tres puntos:

    1) en la invocación siempre hay ques pecificar valores así que tu llamada la puedes hacer así:

    SELECT * FROM dbo.fnTbGalones(null,null)

    2) En el Where los Or debe estar enrtre parentesis (tal como te lo puso Javi) ya que tienen menor prioridad que el And

    3) Para permitir optimización de la sentencia se aconstumbra poner la comparacion con null a la izquierda

    --------

    WHERE

    (@mes IS NULL OR @mes=mes  )

    AND

    (@tipo IS NULL OR @tipo=tipo )

    lunes, 19 de octubre de 2020 13:48

Todas las respuestas

  • Hola German David Quinchia Zapata>

    Si se puede hacer, pero no es lo que te conto tú amigo. 

    Se parece en algo, pero solo se parece. Primero te cuento porque no es lo que te indicaron.

    Que pasa si tú campo tiene un ' ' o lo que es lo mismo, si eso fuera tal que así, jamás se podría filtrar por los que tuvieran un espacio en blanco, y lo mismo para los números, y si quiero solo los que tengan un 0.

    Existen varias maneras de hacer esto. Yo te voy a enseñar una simple. Pero ojo, que depende de la cantidad de campos, esto puede mermar mucho el rendimiento de las búsquedas. 

    Todos hemos pasado por un jefe que quiere que se busque de todo por todos los campos, y que vaya la búsqueda como un tiro, y todo en esta vida no puede ser.

    Create table dbo.autos (mes int, tipo varchar(100), galones int)
    go
    insert into dbo.autos (mes, tipo, galones)
    values
    (1,'AUTOMOVIL', 300),
    (2,'AUUTOBUS', 1000);
    GO

    Solución

    DECLARE @MES INT
    DECLARE @TIPO VARCHAR(100)
    
    SET @MES = 1;
    SET @TIPO = 'AUTOMOVIL'
    
    SELECT * FROM dbo.autos
    	WHERE
    		(@MES = MES OR @MES IS NULL)
    		AND
    		(@TIPO = TIPO OR @TIPO IS NULL);

    Utilizas variables, que comparas con tu columna, en el where, pero si estas son = NULL entonces también valen para no restringir los valores.

    si lo ejecutas sin dar valores a las variables.

    DECLARE @MES INT
    DECLARE @TIPO VARCHAR(100)
    
    SELECT * FROM dbo.autos
    	WHERE
    		(@MES = MES OR @MES IS NULL)
    		AND
    		(@TIPO = TIPO OR @TIPO IS NULL);

    A menudo estas búsquedas se meten dentro de procedimientos almacenados, los cuales, obtendrán un mejor rendimiento, aunque según vaya creciendo la base de datos, puedes tener que recurrir a la opción recompile.

    No te cuento más porque para llegar a ese escenario, tendrás que pegarte mucho aún con este.

    lunes, 19 de octubre de 2020 12:44
  • Otra opción, pero muy importante peligrosa porque puede permitir que te inyecten código SQL, y es muy peligrosa.

    DECLARE @MES INT = 1
    DECLARE @TIPO VARCHAR(100) 
    DECLARE @QUERY NVARCHAR (MAX)
    SET @QUERY = N'
    SELECT * FROM dbo.autos
    	WHERE
    	1 = 1 '+ 
    	  CASE WHEN @MES IS NOT NULL THEN ' AND '+cast(@MES as varchar(2))+' = MES' ELSE '' END
    	+ CASE WHEN @TIPO IS NOT NULL THEN ' AND '''+@TIPO+''' = TIPO ' ELSE '' END
    	+ ';'
    
    EXEC sp_executeSql @QUERY	

    Puedes armar una query dinámica, algo similar a esto, que ejecuta un código al vuelo en función de los parámetros. Esto tiene que estar metido dentro de un procedure, y debería de utilizarse con parámetrosadicionales al sp_executeSQL. 

    Como es algo complejo, y es un escenario que no se da en muchos casos, te lo dejo como una posibilidad para que lo conozcas, pero nada más.

    Si observas el código, se genera una cadena en la variable @query que contendrá la sentencia, y en función de si las variables locales @mes y @tipo tienen contenido, se anexan al where o no. Luego se envía esta a exec sp_executeSql que con la query en la variable, la ejecutará.

    exec sp_executeSql

    https://docs.microsoft.com/es-es/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql?view=sql-server-ver15

    lunes, 19 de octubre de 2020 12:54
  • Hola Javi, muchas gracias por la respuesta.

    esta consulta la metí en una función de tabla la cual me permite jugar con parametros asi:

    CREATE FUNCTION dbo.fnTbGalones
    (
    @mes AS INT,
    @tipo AS VARCHAR(50),
    )
    RETURNS TABLE 
    AS
    RETURN 
    (

    SELECT mes

               ,tipo

               ,galones

    WHERE

    @mes=mes OR @mes IS NULL

    AND

    @tipo=tipo OR @tipo IS NULL

    )

    Cuando trato de hacer la consulta ingresandolo parametros, funciona correctamente asi:

    SELECT *

    FROM dbo.fnTbGalones(1,'AUTOMOVIL'

    Pero cuando no le pongo ningun dato, me sale el siguiente error:

    Mens. 313, Nivel 16, Estado 3, Línea 2
    An insufficient number of arguments were supplied for the procedure or function dbo.fnTbCalcLlanDuracionIdLlantaAcumulada.


    Germanq

    lunes, 19 de octubre de 2020 13:20
  • Hola German:

    ALTER Function dbo.fntbgalones
    				(
    				@Mes As  Int         
    			  , @Tipo As VarChar(50) 
    				)
    Returns Table
    As
    	Return
    	(
    	  Select mes
    		   , tipo
    		   , galones
    			 From dbo.autos
    			 Where 
    				 (@Mes = mes Or @Mes Is Null) 
    			 And (@Tipo = tipo Or @Tipo Is Null)
    	);
    GO

    Te has olvidado de la tabla.

    SELECT * FROM dbo.fntbgalones(null, null) fn

    Otra opción.

    ALTER Function dbo.fntbgalones
    				(
    				@Mes As  Int = null        
    			  , @Tipo As VarChar(50) = null
    				)
    Returns Table
    As
    	Return
    	(
    	  Select mes
    		   , tipo
    		   , galones
    			 From dbo.autos
    			 Where 
    				 (@Mes = mes Or @Mes Is Null) 
    			 And (@Tipo = tipo Or @Tipo Is Null)
    	);
    GO

    Usar default

    SELECT * FROM dbo.fntbgalones(default, default) fn
    A una función hay que pasarle los parámetros que espera. No así a un procedure, donde si estos se definen con un valor por defecto, no es obligatorio suministrarlos.

    lunes, 19 de octubre de 2020 13:36
  • Hola, tres puntos:

    1) en la invocación siempre hay ques pecificar valores así que tu llamada la puedes hacer así:

    SELECT * FROM dbo.fnTbGalones(null,null)

    2) En el Where los Or debe estar enrtre parentesis (tal como te lo puso Javi) ya que tienen menor prioridad que el And

    3) Para permitir optimización de la sentencia se aconstumbra poner la comparacion con null a la izquierda

    --------

    WHERE

    (@mes IS NULL OR @mes=mes  )

    AND

    (@tipo IS NULL OR @tipo=tipo )

    lunes, 19 de octubre de 2020 13:48
  • Muchas gracias Anibal y Javi, ya con esto resolvi la duda.

    Germanq

    lunes, 19 de octubre de 2020 20:09