2012-08-31 1 views
7

TIs ISNULL() une fonction paresseuse?La fonction ISNULL() de Sql Server est-elle paresseuse/court-circuitée?

C'est, si je code quelque chose comme ce qui suit:

SELECT ISNULL(MYFIELD, getMyFunction()) FROM MYTABLE

il sera toujours évaluer getMyFunction() ou sera d'évaluer seulement dans le cas où MYFIELD est en fait nulle?

+0

Vous pouvez ajouter une instruction 'PRINT' à votre fonction et vous en rendre compte par vous-même. –

+0

@JarrettMeyer J'ai utilisé cette technique pour apprendre la programmation dans mon enfance, mais elle ne s'applique pas de nos jours parce que vous ne savez pas ce qu'est le détail de l'implémentation et ce qu'est le comportement documenté. Apprentissage devient difficile :(juste dire ... –

+2

@JarrettMeyer, «PRINT» n'est pas acceptable dans un fichier UDF – Kash

Répondre

2

C'est ce qu'il pense qui fonctionnera le mieux.

Maintenant, il est fonctionnellement paresseux, ce qui est important. Par exemple. si col1 est un varchar qui contient toujours un numéro col2 est nul, alors

isnull(col2, cast(col1 as int)) 

fonctionnera.

Cependant, il n'est pas spécifié s'il essayera le cast avant ou simultanément avec le null-check et mange l'erreur si col2 n'est pas nul, ou s'il essayera seulement le cast du tout si col2 est nul. À tout le moins, nous nous attendons à obtenir col1 dans tous les cas, car un seul balayage d'une table obtenant 2 valeurs va être plus rapide que deux balayages en obtenant un chacun.

Les mêmes commandes SQL peuvent être exécutées de différentes manières, car les instructions que nous donnons sont transformées en opérations de bas niveau basées sur la connaissance des indices et des statistiques sur les tables. Pour cette raison, en termes de performance, la réponse est "quand il semble que ce serait une bonne idée, sinon ce n'est pas le cas".

En termes de comportement observé, il est paresseux. Editer: La réponse de Mikael Eriksson montre qu'il y a des cas qui peuvent en effet être erronés du fait de ne pas être paresseux. Je vais m'en tenir à ma réponse en ce qui concerne l'impact sur les performances, mais il est essentiel en termes d'impact sur la correction dans au moins certains cas.

+0

Vous donnez une bonne explication du fait qu'il peut ou non être paresseux sur les décisions d'optimisation du serveur. Merci. – eidylon

+0

Bienvenue. Bien que je vais ajouter une mise en garde. –

3

A en juger par le comportement différent de

SELECT ISNULL(1, 1/0) 

SELECT ISNULL(NULL, 1/0) 

la première renvoie SELECT 1, la seconde soulève une erreur Msg 8134, Level 16, State 1, Line 4 Divide by zero error encountered..

+0

Ce n'est pas entièrement fiable.Les sous-requêtes peuvent causer des problèmes en raison de différentes règles de réécriture entrant en jeu. – usr

+0

@usr, ils ne provoquera pas la condition d'erreur si le premier champ est toujours null Cela ne veut pas dire que le serveur ne calcule pas 1/0 et mange alors l'erreur quand il voit que l'expression n'est pas utilisée - c'est libre de faire tout ce qu'il veut avec le même comportement extérieur –

+0

@JonHanna, oui mon commentaire était orienté vers des problèmes de performance potentiels – usr

2

Cette fonctionnalité "paresseuse" dont vous parlez est en fait appelée "court-circuit"
Et cela ne fonctionne PAS toujours surtout si vous avez un udf dans l'expression ISNULL.
Vérifiez cet article où les tests ont été effectués pour le prouver:
Short-circuiting (mainly in VB.Net and SQL Server)

T-SQL est un langage déclaratif par conséquent, il ne peut pas contrôler l'algorithme utilisé pour obtenir les résultats .. il déclare que les résultats qu'il a besoin. C'est au moteur de recherche/optimiseur de trouver le plan rentable. Et dans SQL Server, l'optimiseur utilise la "détection de contradiction" qui ne garantit jamais une évaluation de gauche à droite comme vous le feriez dans les langages procéduraux.


Pour votre exemple, a fait un test rapide:
créé l'UDF scalaire à valeur d'invoquer la division par zéro:

CREATE FUNCTION getMyFunction 
(@MyValue INT) 
RETURNS INT 
AS 
BEGIN 
    RETURN (1/0) 
END 
GO 

L'exécution de la requête ci-dessous ne me donne pas une erreur Divide by zero error encountered .

DECLARE @test INT 
SET @test = 1 
SET @test = ISNULL(@test, (dbo.getMyFunction(1))) 
SELECT @test 

Modification du SET à la déclaration ci-après ne me donner l'erreur Divide by zero error encountered.. (Introduit un SELECT dans ISNULL)

SET @test = ISNULL(@test, (SELECT dbo.getMyFunction(1))) 

mais avec des valeurs au lieu des variables, il ne m'a jamais donné l'erreur.

SELECT ISNULL(1, (dbo.getMyFunction(1))) 
SELECT ISNULL(1, (SELECT dbo.getMyFunction(1))) 

Donc, à moins que vous vraiment comprendre comment l'optimiseur évalue ces expressions pour toutes les permutations, il serait prudent de ne pas compter sur les capacités de court-circuit de T-SQL.

4

Cela fonctionne bien

declare @X int 
set @X = 1 
select isnull(@X, 1/0) 

Mais l'introduction d'un agrégat fera échouer et qui prouve que le second argument pourrait être évalué avant la première, parfois.

declare @X int 
set @X = 1 
select isnull(@X, min(1/0)) 
+0

Bel endroit sur les granulats. –