2009-02-10 12 views
22

Quiero poder pasar una lista de parámetros e ignorar los que son NULL. Para que la consulta esté en efecto pretendiendo que el filtro no está allí e ignorándolo.Ignorando un parámetro NULL en T-SQL

lo hacía así:

(@thing IS NULL or [email protected]) 

¿Es esto así, y si es así, habría que realizar mal? Parece ser mucho más lento que construir el SQL por separado.

¿Cuál es la forma óptima de hacer esto?

¡CORREGIDO! Vea la respuesta de Marc Gravell. En resumen, utilizar IS NULL muchas veces es un grande golpe de rendimiento.

Respuesta

19

Una vez que obtenga más de un par de estos, entonces sí: comienza a ser bastante lento. En tales casos, que tienden a utilizar generada TSQL - es decir,

DECLARE @sql nvarchar(4000) 
SET @sql = /* core query */ 

IF @name IS NOT NULL 
    SET @sql = @sql + ' AND foo.Name = @name' 

IF @dob IS NOT NULL 
    SET @sql = @sql + ' AND foo.DOB = @dob' 

// etc 

EXEC sp_ExecuteSQL @sql, N'@name varchar(100), @dob datetime', 
     @name, @dob 

etc

Tenga en cuenta que Sp_executesql almacena en caché de consulta-planes, por lo que cualquier consulta con los mismos argumentos potencialmente puede volver a utilizar el plan.

El inconveniente es que, a menos que firme el SPROC, quien llama necesita permisos SELECT en la tabla (no solo permisos EXEC en el SPROC).

+0

Lo siento, como pregunta noob, ¿a qué te refieres con firmar el sproc? Sin duda, necesito un seleccionar de todos modos para definir la tabla? – Damien

+0

también cómo manejo el hecho de que puede incluir un extra y – Damien

+1

La tabla, etc. sería parte de/* core query */bit (que debe completar). Si aún no tiene un 'WHERE', puede agregar un dummy 'WHERE 1 = 1', o tendrá que usar algo como 'STUFF (...)' (u otra manipulación de cadenas) para cambiar el primer Y a DONDE –

1

No estoy seguro de si es la forma "óptima", pero esto es exactamente lo que hago en mis procedimientos almacenados para los mismos fines. Mi intuición es que esto es más rápido que una consulta creada dinámicamente solo desde el punto de vista de un plan de ejecución. La otra opción es crear una consulta para cada combinación de estos "indicadores" que está pasando, pero eso realmente no es tan escalable.

+0

por plan de ejecución - cosas como Sp_executesql en caché y reutilizar planes de consulta; por lo que un plan * especializado * reutilizado puede superar fácilmente una versión estática generalizada ... –

+0

Nunca he notado una diferencia medible y siempre tomaré legibilidad en unos pocos milis adicionales (a menos que existan requisitos de SLA para el contrario). –

+0

FWIW He ganado mucho dinero de consultoría solo para ayudar a los programadores a pasar de las enormes cláusulas WHERE a las construcciones sp_ExecuteSQL parametrizadas como la forma más rápida de mejorar el rendimiento. Donde los codificadores suelen utilizar una cláusula WHERE de "bits" concatenados que cambian a sp_ExecuteSQL es la respuesta – Kristen

12

Lo manejaría de esta manera.

WHERE Thing = ISNULL(@Thing, Thing) 

Si solo está utilizando el parámetro como un filtro en la cláusula where, esto funcionará muy bien. Ignorará el parámetro si es nulo.

+0

He encontrado, como el OP, que con más de un par de estos mata rápidamente el rendimiento, ya que no puede identificar fácilmente el índice más útil, etc. –

+0

Acepto que el truco de ISNULL es probable que sea un problema de rendimiento: lo desenrollaría como una O para dar al optimizador una mejor oportunidad. También tenga en cuenta que ISNULL adopta el tipo de datos del primer parámetro, por lo que podría tener efectos secundarios extraños. COALESCE probablemente sea una mejor opción. – Kristen

+2

Evitar! No uses este ejemplo. Si pasa nulo para "@Thing" y desea extraer todos los valores (incluido nulo), esto NO funcionará. Si "Cosa" es nula, obtienes nulo = nulo, lo que todos saben que es falso. – MikeTeeVee

0

Este es el método que uso habitualmente. No veo ninguna razón para que sea ineficiente, ya que la declaración debe cortocircuitarse a verdadero si @thing es nulo, y por lo tanto no requerirá un escaneo de tabla. ¿Tiene alguna evidencia de que esta comparación está ralentizando su consulta? Si no, no me preocuparía.

0

cuando declara los parámetros si establece un valor para ellos como nulo en su caso no es necesario que le pase un valor a menos que, por supuesto, lo necesite. Yo uso esta capacidad de bandera si otra consulta hay que ejecutar está en casos especiales cuando el parámetro no es nulo

que normalmente acaba de comprobar como este

campo SI ES NULO

1

Una técnica que he utilizado en el pasado para este escenario es utilizar la función COALESCE como parte de mi cláusula WHERE. Libros en pantalla proporcionará más información detallada sobre la función, pero aquí es un fragmento de la forma en que se puede utilizar en el escenario que usted describe:

create procedure usp_TEST_COALESCE 
(
    @parm1 varchar(32) = null, 
    @parm2 varchar(32) = null, 
    @parm3 int = null 
) 
AS 

SELECT * 
FROM [TableName] 
WHERE Field1 = COALESCE(@parm1, Field1) 
AND Field2 = COALESCE(@parm2, Field2) 
AND Field3 = COALESCE(@parm3, Field3) 

La función COALESCE devolverá la primera expresión no nulo de sus argumentos.En el ejemplo anterior, si alguno de los parámetros es nulo, la función COALESCE usará el valor en el campo subyacente.

Una advertencia importante para usar esta técnica es que los campos subyacentes en la tabla (que conforman su cláusula where) deben ser nulos.

+0

Con los grandes conjuntos de datos que uso, encuentro que este método (aunque limpio) es más lento que (@ parm2 es NULL O Field2 = @ parm2) – BGilman

7

generalmente uso

WHERE (id = @id OR @id IS NULL) 
AND (num = @num OR @num IS NULL) 

etc.

0

Gracias, Esto era muy útil. He decidido utilizar el método sp_ExecuteSQL debido a las posibles ventajas de rendimiento mencionadas. Tengo una opinión un poco diferente que puede ser útil.

DECLARE @sql nvarchar(4000) 
DECLARE @where nvarchar(1000) ='' 

SET @sql = 'SELECT * FROM MyTable' 

IF @Param1 IS NOT NULL 
    SET @where = @where + ' AND Field1 = @Param1' 

IF @Param2 IS NOT NULL 
    SET @where = @where + ' AND Field2 = @Param2' 

IF @Param3 IS NOT NULL 
    SET @where = @where + ' AND Field3 = @Param3' 

-- Add WHERE if where clause exists, 1=1 is included because @where begins with AND 
IF @where <> '' 
    SET @sql = @sql + ' WHERE 1=1' + @where 

--Note that we could also create order parameters and append here 
SET @sql = @sql + ' ORDER BY Field1' 
1

mira el siguiente link en la sección titulada "El Estudio de caso: Los pedidos a partir de". Esto explora todas las opciones en profundidad y debe brindarle una excelente descripción de los costos asociados con cada una de estas opciones. Advertencia, tenga mucho cuidado al usar COALESCE, puede que no le devuelva lo que cree que es.

Saludos,

Tim