25 oct 2014

WHERE condicional

Las consultas a las base de datos son bastante poderosas, pero en algunos casos nos pueden parecer incomodas e incluso frustrantes. Uno de los casos  más comunes es donde necesitamos hacer una consulta donde dado un parámetro si este viene nulo devolver todos los registros pero si trae algún valor hay que devolver los registros filtrados por ese valor, básicamente un filtro condicional.

Luego de varias aproximaciones más o menos eficaces, encontré la que, posiblemente, sea la forma más eficiente de hacer esto sin dividir la consulta en dos (o en 'n' dependiendo de las condiciones) consultas.

Suponiendo que tengo una tabla de productos y que el store procedure recibe un código de categoría para filtrar los resultados, pero si este viene nulo retorna el listado completo de todos los productos, el resultado, usando SQL SERVER 2014,  seria el siguiente:
SELECT P.(...) FROM Producto P
WHERE (1 =  IIF(@pcocCategoria IS NULL, 1, 0) 
OR P.cocCategoria = @pcocCategoria)
En caso de no contar con la instrucción IIF de SQL Server 2014, entonces nos las arreglamos con un CASE:
SELECT P.(...)  FROM Producto P
WHERE (1 = (CASE WHEN @pcocCategoria IS NULL THEN 1 ELSE 0 END) 
OR P.cocCategoria = @pcocCategoria)
En otros caso podría ser dadas una fecha desde y fecha hasta como parámetros necesitemos retornar los registros en ese  rango pero si vienen nulas retornar todos los registros. Entonces utilizaríamos algo similar a esto:
SELECT P.(...)  FROM Producto P
WHERE (1=IIF(@fecDesde IS NULL, 1, 0) OR (P.fecIngreso >= @fecDesde))
AND (1=IIF(@fecHasta IS NULL, 1, 0) OR (P.fecIngreso <= @fecHasta))
Es siempre importante dejar claro que todo este tipo de "workarounds" tiene sus penalizaciones de rendimiento y se debería utilizar únicamente si no existiese alternativas. Siempre es mejor buscar consultas directas, procurando usar los campos índices y en el orden correcto, pero si no queda alternativa aquí queda este tip.