Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

IsNULL et Coalesce bon usage

Cela a été haché et re-haché. En plus de l'astuce que j'ai soulignée dans le commentaire et les liens et l'explication @xQbert postés ci-dessus, sur demande voici une explication de COALESCE vs. ISNULL en utilisant une sous-requête. Considérons ces deux requêtes, qui en termes de résultats sont identiques :

SELECT COALESCE((SELECT TOP (1) name FROM sys.objects), N'foo');

SELECT ISNULL((SELECT TOP (1) name FROM sys.objects), N'foo');

(Commentaires sur l'utilisation de TOP sans ORDER BY pour /dev/null/ merci.)

Dans le cas de COALESCE, la logique est en fait étendue à quelque chose comme ceci :

SELECT CASE WHEN (SELECT TOP (1) ...) IS NULL
    THEN (SELECT TOP (1) ...)
    ELSE N'foo'
END

Avec ISNULL, cela ne se produit pas. Il existe une optimisation interne qui semble garantir que la sous-requête n'est évaluée qu'une seule fois. Je ne sais pas si quelqu'un en dehors de Microsoft sait exactement comment fonctionne cette optimisation, mais vous pouvez le faire si vous comparez les plans. Voici le plan de la version COALESCE :

Et voici le plan pour la version ISNULL - notez à quel point c'est plus simple (et que l'analyse ne se produit qu'une seule fois) :

Dans le cas de COALESCE, l'analyse se produit deux fois. Cela signifie que la sous-requête est évaluée deux fois, même si elle ne donne aucun résultat. Si vous ajoutez une clause WHERE telle que la sous-requête génère 0 ligne, vous verrez une disparité similaire - les formes du plan peuvent changer, mais vous verrez toujours une double recherche + recherche ou une recherche du cas COALESCE. Voici un exemple légèrement différent :

SELECT COALESCE((SELECT TOP (1) name FROM sys.objects 
    WHERE name = N'no way this exists'), N'foo');

SELECT ISNULL((SELECT TOP (1) name FROM sys.objects 
    WHERE name = N'no way this exists'), N'foo');

Le plan pour la version COALESCE cette fois-ci - encore une fois, vous pouvez voir toute la branche qui représente la sous-requête répétée textuellement :

Et encore une fois un plan beaucoup plus simple, faisant environ la moitié du travail, en utilisant ISNULL :

Vous pouvez également consulter cette question sur dba.se pour en savoir plus :

Ma suggestion est la suivante (et vous pouvez voir mes raisons dans le conseil et la question ci-dessus) :faites confiance, mais vérifiez. J'utilise toujours COALESCE (parce qu'il s'agit de la norme ANSI, prend en charge plus de deux arguments et ne fait pas des choses aussi bancales avec la priorité des types de données) à moins que Je sais que j'utilise une sous-requête comme l'une des expressions (dont je ne me souviens pas avoir jamais fait en dehors d'un travail théorique comme celui-ci) ou je rencontre un véritable problème de performances et je veux juste comparer pour voir si COALESCE vs. ISNULL a tout différence de performances substantielle (qui, en dehors du cas de la sous-requête, je n'ai pas encore trouvé). Comme j'utilise presque toujours COALESCE avec des arguments de types de données similaires, j'ai rarement à faire des tests autres que de revenir sur ce que j'en ai dit dans le passé (j'étais aussi l'auteur de l'article aspfaq que xQbert a souligné , il y a 7 ans).