Existe-t-il un moyen d'effectuer une vérification null sur une variable dans une clause WHERE une seule fois?

12

J'ai une requête sur une grande table qui ressemble à ceci:

declare @myIdParam int = 1

select * 
from myTable
where (@myIdParam is null or myTable.Id = @myIdParam)

Il existe plusieurs conditions similaires comme celle-ci dans la clause where, et il existe également de nombreuses jointures, mais il s'agit d'un résumé.

En effet, si @myIdParam est null, nous ne voulons pas restreindre les résultats en utilisant ce paramètre.

Je ne suis pas un pro de la DB, mais d'après mes tests, il semble que cette vérification NULL soit effectuée pour chaque enregistrement et non optimisée de quelque façon que ce soit.

Si je supprime la vérification nulle et suppose que le paramètre n'est pas nul, la requête retourne instantanément. Sinon, cela prend jusqu'à dix secondes.

Existe-t-il un moyen d'optimiser cela afin que la vérification ne soit effectuée qu'une seule fois lors de l'exécution?

Mystagogue
la source
1
Regardez cette réponse: stackoverflow.com/questions/3415582/… tl; dr useOPTION(RECOMPILE)
vercelli
@vercelli cela fait l'affaire. Étant donné que cette question concerne vraiment les paramètres facultatifs, je dirais que c'est un doublon de celui que vous avez lié.
Mystagogue
Probablement, mais c'est un article d'il y a 6 ans. Peut-être qu'avec SqlServer 2014 ou 2016, il y a une nouvelle approche. (Je l'ai testé en 2014 sans recompiler et j'ai pris une éternité)
vercelli
Étant donné que votre requête réelle possède de nombreux paramètres facultatifs, le SQL dynamique fournira les meilleures performances. Voir sommarskog.se/dyn-search.html pour un article complet sur le sujet.
Dan Guzman
@DanGuzman utilisant WITH RECOMPILE comme indiqué dans la question liée à vercelli a réduit le temps de requête d'un peu moins d'une minute à pratiquement instantané avec des critères très sélectifs. Je considère que c'est la meilleure option pour équilibrer les performances et la lisibilité.
Mystagogue

Réponses:

8

Une façon consiste à utiliser SQL dynamique, en utilisant une vérification nulle pour ajouter éventuellement cette partie de la clause where.

declare @myIdParam int = 1
declare @vc_dynamicsql varchar(max)

set @vc_dynamicsql = 'select * from myTable where 1=1'

if @myIdParam is not null
    set @vc_dynamicsql = @vc_dynamicsql + ' and  myTable.Id = @myIdParam'

EXECUTE sp_executesql @vc_dynamicsql
Mystagogue
la source
2
Je préférerais vraiment ne pas le faire, mais c'est une solution. J'espère que quelqu'un viendra avec un bien meilleur.
Mystagogue
1
C'est la meilleure façon de gérer cette classe de requête de recherche. La réponse stackoverflow mentionnée par @vercelli contient d'excellentes références sur la façon de procéder.
Max Vernon
C'est la meilleure méthode mais j'ai remarqué que le paramètre @params pour sp_ExecuteSQLest manquant et que le @vc_dynamicsqlparamètre doit être a NVARCHAR.
James Anderson
4

Chaque fois que vous placez une fonction autour d'une colonne `ISNULL (@var, table.col) 'par exemple, vous supprimez la capacité de SQL à utiliser un index. C'est vraiment l'option la plus performante si vous souhaitez la conserver dans une seule requête.

@var IS NULL or @var = table.col

Sinon, vous avez deux options. Le premier est le SQL dynamique et la réponse de @ Mystagogue est suffisante pour cela sinon vous pouvez poser deux requêtes comme celle-ci:

IF @var is NULL
     SELECT * FROM table
ELSE
     SELECT * FROM table WHERE @var = col

Dans ce format et dans le SQL dynamique, vous obtiendrez en fait un plan de requête différent pour chacune des requêtes (ce qui donnera potentiellement de meilleures performances).

Kenneth Fisher
la source
Le SQL dans la question n'utilise pas ISNULL ou toute autre fonction.
Mystagogue
@MystagogueI Je faisais référence à une réponse maintenant supprimée.
Kenneth Fisher
0

Bien, vous pouvez:

declare @myIdParam int = 1;

select *
from myTable
where nullif(@myIdParam, myTable.Id) is null;

Gardez cependant à l'esprit que la nullif()fonction est essentiellement un wrapper case. Ce n'est pas une solution miracle qui élimine par magie ORet accélère ainsi la requête.

Roger Wolf
la source
l'utilisation de fonctions dans une clause where a un impact négatif sur les performances car elle empêche l'utilisation d'index (ou du moins j'ai entendu parler)
Mystagogue
@Mystagogue, oui - cela rend généralement les conditions de recherche non SARGables. Hélas, c'est la seule façon dont je sais répondre à votre question sans avoir recours au SQL dynamique ou à plusieurs UNIONs. Quand j'ai eu cette tâche exacte, j'ai choisi le SQL dynamique.
Roger Wolf