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

Principales réponses à 5 questions brûlantes sur la fonction COALESCE dans SQL Server

À quel point la fonction COALESCE est-elle cool en SQL ?

C'est assez cool d'être si important pour moi. Et je serai plus qu'heureux d'embaucher un nouveau gars qui n'a pas la mauvaise habitude d'ignorer l'objectif de COALESCE. Cela inclut d'autres expressions et fonctions pour gérer des situations similaires.

Aujourd'hui, vous trouverez les réponses aux cinq questions les plus posées sur l'expression SQL COALESCE. L'un d'eux est débattu encore et encore.

Allons-nous commencer ?

Quelle est l'utilisation de la fonction COALESCE en SQL ?

Il peut être répondu en 2 mots :gestion des valeurs nulles .

Null est un vide de valeurs. Autrement dit, inconnu. Il est différent d'une chaîne vide ou d'un nombre nul. La gestion des valeurs nulles nécessite l'utilisation d'expressions et de fonctions. L'un d'eux est COALESCE.

Pour comprendre ce que je veux dire, consultez les déclarations ci-dessous :

DECLARE @number INT

SELECT @number + 33587

Fonctionnera-t-il bien ? Ce sera le cas.

Y a-t-il un problème? Aucun, pour le moment.

Mais les instructions donneront NULL car l'ajout de null à un nombre équivaut à NULL.

Si toutes vos requêtes ne tombent qu'à ce niveau, vous pouvez arrêter de lire. Mais bien sûr, ils ne le sont pas. Nous sommes payés plus que pour produire ce genre de code.

Maintenant, ajoutons un peu de "catastrophe" :

DECLARE @number INT

SET @number = @number + 33587

UPDATE myTable
set col1 = @number   -- Surprise! col1 is NOT NULLABLE
where ID = 1

PRINT 'Success!'

L'exécution du code ci-dessus sera dans l'impasse lorsqu'il atteindra l'instruction UPDATE. Il n'imprimera pas 'Success!' car vous ne pouvez pas mettre un null sur une colonne non nullable. Cette déclaration explique-t-elle clairement pourquoi nous devons gérer les valeurs nulles ?

Modifions le code pour ajouter un filet de sécurité :

DECLARE @number INT

SET @number = COALESCE(@number,0) + 33587     -- our safety net. Thanks to COALESCE.

UPDATE myTable
set col1 = @number               -- Disaster averted!
where ID = 1

PRINT 'Success!'

COALESCE changera la valeur nulle en zéro et une somme ne sera pas nulle.

La leçon est que COALESCE est l'un des filets de sécurité contre les nulls. Mieux encore, gérer correctement les valeurs nulles dans votre code SQL réduit vos maux de tête et vous permet de rentrer chez vous plus tôt. C'est certain.

Vous comprenez maintenant pourquoi je veux dans mon équipe quelqu'un qui s'applique à gérer les valeurs nulles.

Plus d'exemples pratiques dans l'utilisation de SQL COALESCE

Reportons-nous à des exemples plus pratiques.

Supposons que vous viviez dans une région où certaines personnes ont des prénoms, mais d'autres non. Comment allez-vous former le nom complet à partir du prénom, du deuxième prénom et du nom de famille sans tomber dans le piège du nul ?

Voici une solution possible avec COALESCE :

USE AdventureWorks
GO

SELECT
p.LastName + ', ' + p.FirstName + ' ' + COALESCE(p.MiddleName + ' ','') AS FullName
FROM Person.Person p

Autre exemple :supposez que vous êtes un employé d'une entreprise où le salaire brut est calculé différemment pour chaque employé. Pour certains d'entre eux, il existe des tarifs horaires. D'autres sont payés à des taux hebdomadaires ou mensuels.

Voici un exemple de tableau avec une solution de requête utilisant COALESCE :

-- STEP 1: Create the table
CREATE TABLE EmployeeWages (
    employee_id INT PRIMARY KEY,
    hourly_rate SMALLMONEY,
    weekly_rate SMALLMONEY,
    monthly_rate MONEY,
    CHECK(
        hourly_rate IS NOT NULL OR
        weekly_rate IS NOT NULL OR
        monthly_rate IS NOT NULL)
);

-- STEP 2: Insert data
INSERT INTO
    EmployeeWages(
        employee_id,
        hourly_rate,
        weekly_rate,
        monthly_rate
    )
VALUES
    (1,60, NULL,NULL),
    (2,40, NULL,NULL),
    (3,NULL, 1000,NULL),
    (4,NULL, NULL,7000),
    (5,NULL, NULL,5000);

-- STEP 3: Query the monthly salary.
SELECT
    employee_id,
    COALESCE(
        hourly_rate*22.00*8.00,
        weekly_rate*4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

Le tableau contient différents modes de rémunération par identifiant d'employé. La requête vous demande de générer un salaire mensuel pour tous.

C'est là que COALESCE brillera :il accepte une liste de valeurs, et il peut y avoir n'importe quel nombre d'éléments. COALESCE choisira le premier qui n'est pas nul :

Sympa, n'est-ce pas ?

Comment COALESCE fonctionne-t-il en SQL ?

La définition de COALESCE est une expression qui renvoie la première valeur non nulle d'une liste de valeurs. La syntaxe COALESCE est :

COALESCE ( expression [ ,…n ] )

Notre exemple précédent avec différents modes de paiement des salaires illustre cela.

Ce qu'il y a sous le capot

Sous le capot, la fonction COALESCE en SQL est une expression enrobée de sucre pour une expression CASE beaucoup plus longue. Cela élimine le besoin de taper un CASE équivalent, qui est plus long (et fatiguant, pour les dactylographes paresseux comme moi). Le résultat sera le même.

Peut-on le prouver ? Oui! D'une part, Microsoft l'admet.

Mais tant mieux pour nous, Microsoft l'a inclus dans le plan d'exécution. Ainsi, nous savons ce qui se passe.

Essayons-le en utilisant un exemple précédent avec des salaires. Mais avant de relancer la requête ci-dessous, activez Inclure le plan d'exécution réel ou appuyez simplement sur CTRL-M .

SELECT
    employee_id,
    COALESCE(
        hourly_rate * 22.00 * 8.00,
        weekly_rate * 4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

Cliquez sur le plan d'exécution onglet dans les résultats. Cela semble simple, mais notre joyau caché réside dans le Compute Scalar nœud. Lorsque vous passez la souris dessus, vous voyez une expression nommée Expr1002 (Figure 2). Qu'est-ce que cela pourrait être ?

Creusons plus profondément. Faites un clic droit dessus et sélectionnez Afficher le XML du plan d'exécution . Une nouvelle fenêtre apparaîtra. Jetez un œil à la figure 3 ci-dessous :

Il y a votre déclaration CASE. Vous trouverez ci-dessous le tout formaté et mis en retrait pour plus de lisibilité :

CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                            *(22.00)*(8.00) IS NOT NULL
     THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                       [TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),0)
     ELSE CASE WHEN    
          CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
                                            *(4.00) IS NOT NULL
          THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),                                                         
                       [TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0)
          ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)
          END
END

C'est assez long par rapport à

COALESCE(
        hourly_rate * 22.00 * 8.00,
        weekly_rate * 4.00,
        monthly_rate
        )

C'est ce que SQL Server a fait avec notre requête avec COALESCE. Tout sert à obtenir la première valeur qui n'est pas nulle dans une liste de valeurs.

Peut-il être plus court ?

Je sais ce que tu penses. Si SQL Server le fait pendant le traitement de la requête, COALESCE doit être lent. Sans oublier les multiples apparitions de CONVERT_IMPLICIT. Allez-vous plutôt utiliser des alternatives ?

D'une part, vous pouvez taper vous-même une instruction CASE plus courte. Ou, vous pouvez utiliser ISNULL. Plus sur cela plus tard. En parlant de lenteur, j'en avais parlé avant la fin de cet article.

Quelles sont les différences entre COALESCE et ISNULL en SQL ?

L'une des alternatives à COALESCE est ISNULL. L'utilisation de COALESCE avec 2 valeurs le rend similaire à ISNULL. Au moins, les résultats semblent similaires. Pourtant, il existe des différences notables. Vous pouvez l'utiliser comme guide pour décider si vous utiliserez COALESCE ou ISNULL.

(1) ISNULL Accepte 2 Arguments. COALESCE accepte une liste d'arguments

C'est la différence la plus flagrante. De la syntaxe, c'est sûr que c'est différent.

ISNULL ( check_expression , replacement_value )
COALESCE ( expression [ ,…n ] )

Lorsque vous utilisez les deux avec 2 arguments, les résultats sont les mêmes. Les 2 déclarations ci-dessous donneront 1 :

SELECT ISNULL(NULL, 1)
SELECT COALESCE(NULL, 1)

Bien que les résultats soient les mêmes, ils sont signifiés différemment :

  • ISNULL(NULL, 1) a renvoyé 1 car le premier argument est NULL.
  • COALESCE(NULL, 1) a renvoyé 1 car 1 est la première valeur non nulle de la liste .

(2) COALESCE est la norme SQL-92

C'est exact. Tu peux le vérifier. Par exemple, si vous souhaitez porter votre code SQL vers MySQL à partir de SQL Server, COALESCE fonctionnera de la même manière. Voir la figure 4 et comparer le résultat de la figure 1 :

Cependant, l'utilisation d'ISNULL dans MySQL déclenchera une erreur si vous utilisez la syntaxe de SQL Server.

par exemple, exécutez ce qui suit dans SQL Server Management Studio et MySQL Workbench :

SELECT ISNULL(null,1)

Qu'est-il arrivé? Dans SQL Server, la sortie est 1. Mais dans MySQL, la sortie est une erreur :

06:36:52 SELECT ISNULL(null,1) Code d'erreur :1582. Nombre de paramètres incorrect dans l'appel à la fonction native "ISNULL"

Le fait est que ISNULL dans MySQL accepte 1 argument et renvoie 1 si l'argument est nul. Sinon, il renvoie 0.

(3) Le passage de 2 valeurs nulles dans COALESCE déclenche une erreur. Tout va bien avec ISNULL

Cela déclenchera une erreur :

SELECT COALESCE(NULL, NULL)

L'erreur est :"Au moins un des arguments de COALESCE doit être une expression qui n'est pas la constante NULL."

Ce sera très bien :

SELECT ISNULL(NULL, NULL)

Changer le code COALESCE en quelque chose de similaire ci-dessous ne déclenchera pas d'erreur :

DECLARE @value INT = NULL
SELECT COALESCE(@value,null)

Cela s'est produit parce que @value n'est pas une constante nulle.

(4) SQL COALESCE est converti en CASE. ISNULL reste ISNULL

Nous l'avons vu dans le "Comment COALESCE fonctionne-t-il en SQL ?" section. Ici, examinons un autre exemple :

SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Inspection du plan d'exécution XML pour l'opérateur scalaire révèle la conversion en CASE :

 [AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+CASE WHEN ([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ') IS NOT NULL
      THEN [AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' '
      ELSE N''
 END

Maintenant, exécutez une requête équivalente en utilisant ISNULL :

SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Ensuite, inspectez le Execution Plan XML pour l'opérateur scalaire :

 [AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+isnull([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ',N'')

ISNULL est toujours ISNULL.

(5) Le type de données de l'expression résultante est différent

La détermination du type de données de l'expression résultante est également différente entre COALESCE et ISNULL :

  • ISNULL utilise le type de données du premier paramètre.
  • COALESCE renvoie le type de données de la valeur avec la priorité la plus élevée.

Pour une liste de priorité des types de données, consultez ce lien.

Prenons un exemple :

SELECT
 employee_id
,COALESCE(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages

Ensuite, inspectez la conversion en CASE dans le Execution Plan XML :

CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
                                           *CONVERT_IMPLICIT(smallmoney,[@1],0),0) IS NOT NULL
     THEN CONVERT_IMPLICIT(numeric(19,4), CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
                                           *CONVERT_IMPLICIT(smallmoney,[@1],0),0),0)
     ELSE (0.0000)
END

Dans l'expression CASE ci-dessus, le type de données du résultat sera numeric(19,4).

Pourquoi? Il a une priorité plus élevée que l'argent et petite monnaie même si vous le CASTEZ pour de l'argent . Pourquoi numérique et non de l'argent ? A cause de la constante 0.0000.

Au cas où vous vous demanderiez ce qu'est @1, le Execution Plan XML a la réponse. C'est le nombre constant 4.

<ParameterList>
 <ColumnReference Column="@1" ParameterDataType="int" ParameterCompiledValue="(4)"  
       ParameterRuntimeValue="(4)" />
</ParameterList>

Essayons avec ISNULL :

SELECT
 employee_id
,ISNULL(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages

Encore une fois, recherchez l'opérateur scalaire 's ScalarString :

ISNULL(CONVERT(MONEY,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]*($4.0000),0),($0.0000))

Enfin, le type de données de l'expression résultante sera money . C'est le type de données du premier argument.

Comment déjouer la priorité des données

Vous pouvez "déjouer" la priorité des données en ajoutant quelques modifications à votre code. Auparavant, le résultat avait une valeur numérique Type de données. Si vous voulez que le résultat soit un argent type de données et supprimez le CONVERT_IMPLICIT, procédez comme suit :

SELECT
 employee_id
,COALESCE(CAST(weekly_rate AS MONEY) * ($4.0000),($0.0000)) AS monthly_rate
FROM EmployeeWages

Avez-vous remarqué les constantes ($4,000) et ($0,0000) ? C'est de l'argent constantes. Ce qui se passe ensuite apparaît dans le XML du plan d'exécution 's ScalarString :

CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000) IS NOT NULL 
     THEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000) 
     ELSE ($0.0000) 
END

C'est beaucoup mieux. C'est plus court et le CONVERT_IMPLICIT a disparu. Et le type de données résultant est money .

(6) La capacité NULL de l'expression résultante est différente

ISNULL(NULL, 1) et COALESCE(NULL, 1) ont des résultats similaires, mais leurs valeurs de nullabilité sont différentes. COALESCE est nullable. ISNULL ne l'est pas. Vous pouvez le voir lorsque vous l'utilisez sur des colonnes calculées.

Référons-nous à un exemple. L'instruction ci-dessous déclenchera une erreur car la PRIMARY KEY ne peut pas accepter les valeurs NULL. Dans le même temps, la nullabilité de l'expression COALESCE pour column2 évalue à NULL.

CREATE TABLE NullabilityDemo  
(  
  column1 INTEGER NULL,  
  column2 AS COALESCE(column1, 0) PRIMARY KEY,  
  column3 AS ISNULL(column1, 0)  
);

Cette instruction réussit car la possibilité de nullité de la fonction ISNULL évalue AS NOT NULL.

CREATE TABLE NullabilityDemo  
(  
  column1 INTEGER NULL,  
  column2 AS COALESCE(column1, 0),  
  column3 AS ISNULL(column1, 0) PRIMARY KEY  
);

(7) L'argument de gauche d'ISNULL est évalué une fois. C'est le contraire avec COALESCE

Reprenez l'exemple précédent :

SELECT
    employee_id,
    COALESCE(
        hourly_rate*22.00*8.00,
        weekly_rate*4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

Ensuite, inspectez le ScalarString pour cela :

CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                              *(22.00)*(8.00) IS NOT NULL 
     THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                                              [TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                              *(22.00)*(8.00),0) 
     ELSE CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
                                              *(4.00) IS NOT NULL 
               THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                                        [TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0) 
               ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0) 
          END 
END

Lorsque la fonction COALESCE en SQL est convertie en CASE, chaque expression est évaluée deux fois (sauf la dernière). Comme vous pouvez le voir ci-dessus, hourly_rate*22.00*8.00 apparu deux fois. Même chose avec weekly_rate*4.00 . La dernière expression, monthly_rate , est apparu une fois.

Étant donné que COALESCE évaluera les expressions deux fois, il peut y avoir une baisse des performances. Plus d'informations à ce sujet plus tard.

Cependant, consultez l'équivalent ISNULL :

SELECT
 employee_id,
 ISNULL(hourly_rate * 22.00 * 8.00,ISNULL(weekly_rate * 4.00,monthly_rate)) AS  
                                                       monthly_salary
FROM EmployeeWages

Ensuite, nous inspectons le ScalarString dans le plan d'exécution XML :

isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),
       CONVERT_IMPLICIT(numeric(19,8),
isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),
CONVERT_IMPLICIT(numeric(14,6),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)),0))

Comme vous pouvez le voir ci-dessus, hourly_rate , taux_hebdomadaire , et taux_mensuel n'est apparu qu'une seule fois. Ainsi, vous n'avez pas à vous soucier des expressions évaluées deux fois avec ISNULL.

Pouvons-nous utiliser COALESCE dans une clause WHERE ?

Chose sûre. Il n'y a pas de meilleur moyen que de montrer un exemple pour le prouver.

-- Query all the names in Person table with no middle name

USE AdventureWorks
GO

SELECT
 p.LastName
,p.FirstName
FROM person.Person p
WHERE COALESCE(p.MiddleName,'') = ''

COALESCE renverra une chaîne vide si MiddleName est NUL. Bien sûr, il existe un moyen plus court de produire le résultat. Mais cela montre que COALESCE fonctionne dans une clause WHERE.

Qu'est-ce qui est le plus rapide :COALESCE ou ISNULL ?

Enfin, nous arrivons à un sujet brûlant :les performances !

Vous obtiendrez de nombreuses pages avec des tests et des comparaisons, mais il y aura une bataille de partisans de COALESCE et ISNULL dans les commentaires. Nous éliminerons la poussière et la fumée de ces guerres.

Qu'est-ce qui est le plus rapide :COALESCE ou ISNULL ? Permettez-moi de commencer par dire que la réponse est :

(tambours roulants)

CELA DÉPEND !

(mâchoire baissée)

Déçu? Je vais l'expliquer dans un instant.

Tout d'abord, je reconnais que les deux semblent avoir des différences de performances si vous utilisez le temps écoulé comme métrique. Certaines personnes ont pris en charge ce fait lorsque SQL Server traduit les instructions COALESCE en CASE. Pendant ce temps, ISNULL reste tel quel.

D'autres peuvent raisonner différemment en raison des résultats variables. De plus, pour eux, convertir COALESCE en CASE est plus rapide que de cligner des yeux. Tout comme ce qui est souligné dans ce fil, la différence de performances "est minuscule". Je suis d'accord. Voici un autre article qui disait que la différence "est mineure".

Cependant, voici un gros problème :pouvons-nous faire confiance à un minuscule temps écoulé comme métrique ? Ce qui compte vraiment, c'est le nombre de lectures logiques de la requête. C'est ce que nous allons souligner dans nos prochains exemples.

(Consultez mon autre article sur les lectures logiques et pourquoi ce facteur retarde vos requêtes.)

Exemple 1

Examinons le même exemple et comparons leurs lectures logiques et leurs plans d'exécution. Avant de l'exécuter, assurez-vous que STATISTICS IO est activé et incluez le plan d'exécution réel est activé.

SET STATISTICS IO ON

SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Voici les faits :

Les lectures logiques pour les deux requêtes sont les mêmes. Les deux font 107 * 8 Ko de pages. Si nous avons un million d'enregistrements, les lectures logiques augmenteront, bien sûr. Mais les lectures logiques pour les deux requêtes seront égales. C'est le cas même si COALESCE est converti en CASE :

Examinons le plan d'exécution. Voici comment nous allons procéder :

  1. Exécutez la 1ère instruction SELECT avec l'expression COALESCE.
  2. Cliquez sur Plan d'exécution onglet dans les résultats. Faites un clic droit dessus et sélectionnez Enregistrer le plan d'exécution sous . Et nommez le fichier plan1.sqlplan .
  3. Exécutez la 2ème instruction SELECT avec la fonction ISNULL.
  4. Cliquez sur Plan d'exécution onglet dans les résultats.
  5. Cliquez dessus avec le bouton droit de la souris et sélectionnez Comparer Showplan .
  6. Sélectionnez le fichier plan1.sqlplan . Une nouvelle fenêtre apparaîtra.

C'est ainsi que nous allons inspecter le plan d'exécution pour tous les exemples.

Pour en revenir à notre premier exemple, reportez-vous à la figure 6 pour voir la comparaison du plan d'exécution :

Avez-vous remarqué ces points importants dans la figure 6 ?

  • La partie ombrée des 2 plans (les analyses d'index) signifie que SQL Server a utilisé les mêmes opérations pour les 2 requêtes.
  • Le QueryPlanHash pour les 2 plans est 0x27CEB4CCE12DA5E7, ce qui signifie que le plan est le même pour les deux.
  • Les différences de quelques millisecondes pour le temps écoulé sont négligeables.

À emporter

C'est comme comparer des pommes avec des pommes, mais de types différents. L'une est une pomme Fuji du Japon, une autre est une pomme rouge de New York. Pourtant, ce sont toutes les deux des pommes.

De même, SQL Server nécessite les mêmes ressources et le plan d'exécution choisi pour les deux requêtes. La seule différence est l'utilisation de COALESCE ou ISNULL. Différences mineures, car le résultat final est le même.

Exemple 2

La grande différence apparaît lorsque vous utilisez une sous-requête comme argument à la fois pour COALESCE et ISNULL :

USE AdventureWorks
GO

SELECT COALESCE(
       (SELECT
        SUM(th.ActualCost)
        FROM Production.TransactionHistory th
        WHERE th.ProductID = 967)
       ,0) 

SELECT ISNULL(
       (SELECT
        SUM(th.ActualCost)
        FROM Production.TransactionHistory th
        WHERE th.ProductID = 967)
       ,0)

Le code ci-dessus aura le même résultat, mais l'intérieur est très différent.

Commençons par les lectures logiques :

L'instruction SELECT avec l'expression COALESCE a le double des lectures logiques de celle qui a utilisé ISNULL.

Mais pourquoi doubler les lectures logiques ? La comparaison des plans d'exécution en révélera encore plus :

La figure 8 explique pourquoi les lectures logiques sont doublées avec COALESCE. Voir les 2 nœuds Stream Aggregate dans le plan du bas :ce sont des doublons. La question suivante est, pourquoi a-t-il été dupliqué ?

Rappelons que le point concerne le moment où COALESCE est converti en CASE. Combien de fois les expressions sont-elles évaluées dans les arguments ? DEUX FOIS !

Ainsi, la sous-requête est évaluée deux fois. Il apparaît dans le plan d'exécution avec des nœuds en double.

Cela explique également les doubles lectures logiques utilisant COALESCE par rapport à ISNULL. Si vous comptez regarder le plan d'exécution XML de la requête avec COALESCE, c'est plutôt long. Mais cela révèle que la sous-requête sera exécutée deux fois.

Maintenant quoi? Pouvons-nous déjouer cela? Bien sûr! Si vous avez déjà fait face à quelque chose comme ça, ce qui, je pense, est rare, la solution possible est de diviser pour mieux régner. Ou, utilisez ISNULL s'il ne s'agit que d'une seule sous-requête.

Comment éviter d'évaluer deux fois l'expression de la sous-requête

Voici comment éviter d'évaluer la sous-requête deux fois :

  • Déclarez une variable et affectez-lui le résultat de la sous-requête.
  • Ensuite, transmettez la variable en tant qu'argument à COALESCE.
  • Répétez les mêmes étapes en fonction du nombre de sous-requêtes.

Comme je l'ai dit, cela devrait être rare, mais si cela se produit, vous savez quoi faire maintenant.

Quelques mots sur le niveau d'isolement

L'évaluation de la sous-requête deux fois peut entraîner un autre problème. Selon le niveau d'isolement de votre requête, le résultat de la première évaluation peut être différent de la seconde dans un environnement multi-utilisateurs. C'est fou.

Pour garantir le retour de résultats stables, vous pouvez essayer d'utiliser un SNAPSHOT ISOLATION. Vous pouvez également utiliser ISNULL. Ou, il peut s'agir d'une approche consistant à diviser pour mieux régner, comme indiqué ci-dessus.

À emporter

Alors, quelle est l'essence ?

  • Vérifiez toujours les lectures logiques. Cela compte plus que le temps écoulé. Utilisez le temps écoulé et emportez votre santé mentale. Votre machine et le serveur de production auront toujours des résultats différents. Accordez-vous une pause.
  • Vérifiez toujours le plan d'exécution afin de savoir ce qui se passe sous le capot.
  • Sachez que lorsque COALESCE est traduit en CASE, les expressions sont évaluées deux fois. Ensuite, une sous-requête ou quelque chose de similaire peut poser problème. Affecter le résultat de la sous-requête à une variable avant de l'utiliser dans COALESCE peut être une solution.
  • Ce qui est plus rapide dépend des résultats des lectures logiques et des plans d'exécution. Il n'y a pas de règle générale pour déterminer si COALESCE ou ISNULL est plus rapide. Sinon, Microsoft pourrait en informer, comme ils l'ont fait dans HierarchyID et SQL Graph.

Finalement, ça dépend vraiment.

Conclusion

J'espère que vous avez eu une lecture intéressante de cet article. Voici les points dont nous avons discuté :

  • COALESCE est l'un des moyens de gérer les valeurs nulles. C'est un filet de sécurité pour éviter les erreurs de code.
  • Il accepte la liste d'arguments et renvoie la première valeur non nulle.
  • Elle est convertie en une expression CASE lors du traitement de la requête, mais elle ne ralentit pas la requête.
  • Bien qu'il existe quelques similitudes avec ISNULL, il existe 7 différences notables.
  • Il peut être utilisé avec la clause WHERE.
  • Enfin, ce n'est ni plus rapide ni plus lent qu'ISNULL.

Si vous aimez cet article, les boutons des réseaux sociaux attendent votre clic. Partager c'est prendre soin.

Merci.

Lire aussi

Gérer efficacement les valeurs NULL avec la fonction SQL COALESCE pour les débutants

Une utilisation pratique de la fonction SQL COALESCE