IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Développement SQL Server Discussion :

Problème CASE avec multiples conditions dont IS NULL


Sujet :

Développement SQL Server

  1. #1
    Candidat au Club
    Inscrit en
    Mai 2011
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Mai 2011
    Messages : 4
    Points : 2
    Points
    2
    Par défaut Problème CASE avec multiples conditions dont IS NULL
    Bonjour,

    Je souhaite tester si COLONNE1 et COLONNE2 sont vides ou contiennent un espace ' '

    J'ai testé cette requête mais j'ai une erreur.
    C'est apparemment le IS NULL qui pose problème car si je le remplace par 'abc' ça fonctionne.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    UPDATE MA_TABLE_TEST SET COLONNE1 =
    
    ( CASE COLONNE2
    WHEN 'VAR1' THEN
    CASE WHEN COLONNE3 in (IS NULL,' ') THEN 'OK1'
    ELSE 'OK2'
    END
    WHEN 'VAR2' THEN
    CASE WHEN COLONNE3 in (IS NULL,' ') THEN 'OK3'
    ELSE 'OK4'
    END
    END
    )
    Pourriez-vous m'aider svp ?

    Merci

  2. #2
    Invité
    Invité(e)
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    CASE WHEN COLONNE3 IS NULL OR COLONNE3 = ' ' THEN 'OK1'

  3. #3
    Candidat au Club
    Inscrit en
    Mai 2011
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Mai 2011
    Messages : 4
    Points : 2
    Points
    2
    Par défaut
    Citation Envoyé par 7gyY9w1ZY6ySRgPeaefZ Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    CASE WHEN COLONNE3 IS NULL OR COLONNE3 = ' ' THEN 'OK1'
    Merci !
    Ça fonctionne maintenant

  4. #4
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Tu peux aussi utiliser cette syntaxe :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    CASE WHEN coalesce(COLONNE3, ' ') = ' ' THEN 'OK1'

    L'avantage c'est que tu peux indexer coalesce(COLONNE3, ' ') et du coup la condition devient sargable.
    On ne jouit bien que de ce qu’on partage.

  5. #5
    Membre émérite
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Points : 2 498
    Points
    2 498
    Par défaut
    Dans le cas present ISNULL suffit et est sans doute meme plus performant :-)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    CASE WHEN ISNULL(COLONNE3, ' ') = ' ' THEN 'OK1'
    Comme ça tu as deux possibilités
    L'avantage que peut avoir le COALESCE c'est qu'il permet les parametres en cascade

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    COALESCE(COLONNE3,COLONNE2,COLONNE1,' ')=' '
    Le premier non NULL à gagné :-)
    « Ils ne savaient pas que c'était impossible, alors ils l'ont fait ». (Twain)

  6. #6
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par olibara Voir le message
    Dans le cas present ISNULL suffit et est sans doute meme plus performant :-)
    Faudrait argumenter un peu plus, surtout pour la performance, parce que pour moi, c'est blanc bonnet et bonnet blanc.

  7. #7
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par 7gyY9w1ZY6ySRgPeaefZ Voir le message
    Faudrait argumenter un peu plus, surtout pour la performance, parce que pour moi, c'est blanc bonnet et bonnet blanc.
    "On" m'a déjà dit (SQLPro faisait partie du "on") la même chose.

    Le ISNULL(), spécifique à SQL Server, serait plus optimisé et parfois changerait même complètement le plan d'exécution.
    J'avoue ne pas comprendre non plus comment SQL Server peut ne pas considérer COALESCE(arg1, arg2) comme un simple alias de ISNULL(arg1, arg2).

    Je suppose, mais sans en être complètement certain, qu'il existe un ou plusieurs cas où les deux fonctions ne font pas la même chose :
    - mixité des types
    - résultat forcément non null
    - autre ?

    L'exemple ci-dessous (avec SQL Server 2016 Express) montre effectivement une différence de comportement avec les arguments NULL :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    select isnull(1, 2);
    select isnull(null, null);
    select isnull(1, 'a');
     
    select coalesce(1, 2);
    select coalesce(null, null);
    select coalesce(1, 'a');
    Msg 4127, Level 16, State 1, Line 6
    Au moins un des arguments de COALESCE doit être une expression qui n'est pas la constante NULL.

    Le ISNULL(null, null) ne pose pas de problème pourtant. Visiblement, COALESCE() a un type de retour "not null".

    Pourtant, l'exemple suivant passe sans problème :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    declare @a int;
    declare @b int;
    set @a = null;
    set @b = null;
    select coalesce(@a, @b);

    Donc il faut au moins un argument littéral non null, mais si c'est une variable (ou une colonne, j'imagine) ça passe sans problème avec que des null...
    Très étrange...

    Ah, ou alors j'ai compris ! C'est une histoire de type !

    Il faut au moins une variable typée (donc différence de la valeur littérale "null" qui n'est pas typée) pour que coalesce sache quel type retourner.
    Pourtant, passer des arguments de type différent ne le fait pas sauter...
    Étrange...

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    declare @a int;
    declare @b varchar;
    set @a = 1;
    set @b = 'a';
    select coalesce(@a, @b);
    select isnull(@a, @b);
    Ça marche

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    declare @a int;
    declare @b varchar;
    set @a = 1;
    set @b = null;
    select coalesce(@a, @b);
    select isnull(@a, @b);
    Ca marche aussi

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    declare @a int;
    declare @b varchar;
    set @a = null;
    set @b = 'a';
    select coalesce(@a, @b);
    select isnull(@a, @b);
    Ca plante pour isnull aussi bien que pour coalesce...

    J'avoue ne pas comprendre la logique profonde du truc...
    On ne jouit bien que de ce qu’on partage.

  8. #8
    Invité
    Invité(e)
    Par défaut
    Merci bien de l'explication !
    Il faudrait que MS fasse un gros ménage dans les fonctions SQL Server, il y a tellement de vielles fonctions bancales toujours en activité.
    C'est là qu'Oracle a encore une longueur d'avance avec des librairies bien plus solides et plus fournies mais pas non plus complètement au dessus de tout soupçon - j'ai déjà débuggé et corrigé une de leur fonction...

  9. #9
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 768
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 768
    Points : 52 571
    Points
    52 571
    Billets dans le blog
    5
    Par défaut
    Tu rigoles ou quoi ? C'est farci de fonctions redondantes propriétaires imbitables à commencer par les TO_machin..... !

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  10. #10
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Points : 13 092
    Points
    13 092
    Par défaut
    bonjour,

    Citation Envoyé par StringBuilder Voir le message
    Le ISNULL(), spécifique à SQL Server, serait plus optimisé et parfois changerait même complètement le plan d'exécution.
    C'est en effet le cas.
    Citation Envoyé par StringBuilder Voir le message
    J'avoue ne pas comprendre non plus comment SQL Server peut ne pas considérer COALESCE(arg1, arg2) comme un simple alias de ISNULL(arg1, arg2).
    Justement parce que les deux fonctions ne sont pas tout à fait identiques même si elles adressent le même besoin.
    Citation Envoyé par StringBuilder Voir le message
    Je suppose, mais sans en être complètement certain, qu'il existe un ou plusieurs cas où les deux fonctions ne font pas la même chose :
    - mixité des types
    - résultat forcément non null
    - autre ?
    Le type de retour n'est en effet pas le même.
    - ISNULL renvoie le type du premier argument
    - COALESCE renvoie le type de l'argument ayant la plus forte précédence :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    DECLARE @a VARCHAR(3)
    DECLARE @b VARCHAR(4) = 'toto'
     
    SELECT COALESCE(@a, @b) AS r_coalesce, ISNULL(@a, @b) as r_isnull
    ==>
    r_coalesce	r_isnull
    toto	        tot
    
    Il existe également un différence de comportement notable :
    Avec un COALESCE, les expressions sont interprétées une fois pour tester la nullité, et une deuxième fois pour renvoyer la valeur non NULL, ce qui n'est pas vraiment génant quand on teste une simple colonne, mais peut le devenir quand on fait appel a une sous requête, ou une fonction couteuse :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    CREATE FUNCTION UDF_TEST ()
    RETURNS INT
    AS
    BEGIN
    DECLARE @x int = 0;
    	WHILE (@x < 1000000) SET @x += 1;
    	RETURN 0;
     
    END
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT ISNULL(dbo.udf_test(), 0)
     SQL Server \endash Temps d'exécution*: 
    , Temps UC = 452*ms, temps écoulé = 587*ms.
    
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT COALESCE(dbo.udf_test(), 0)
     SQL Server \endash Temps d'exécution*: 
    , Temps UC = 889*ms, temps écoulé = 1239*ms.
    
    Cela peut même donner des résultats inattendus :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    CREATE FUNCTION UDF_TEST (@i VARCHAR(50))
    RETURNS INT
    AS
    BEGIN
     
    	RETURN CASE WHEN @i like '[0-8]%' THEN NULL ELSE 1 END;
     
    END
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT COALESCE(dbo.udf_test(NEWID()), 0)
    peut parfois renvoyer :
    NULL
    
    Alors qu'un ISNULL renverra toujours 0 ou 1;

  11. #11
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Merci beaucoup pour cette explication parfaitement limpide

    Reste à comprendre pourquoi COALESCE est aussi mal implémenté (car autant pour le type de retour, je comprends la différence, entre l'implémentation historique non standard, et l'implémentation normée, autant le coup de la double évaluation dénote une grave anomalie d'optimisation !)

    Et surtout, comme indiqué avec l'appel à une fonction non déterministe, le résultat de COALESCE est tout simplement :
    - faux (puisque ça ne ramène pas la valeur initiale, mais la valeur calculée une seconde fois)
    - illogique (puisque ça peut ramener NULL alors que c'est justement fait pour l'éviter !)

    Mon intégrisme du "tout normé" en prend un coup Je crois que les ISNULL() vont fleurir un peu partout dans mon code
    On ne jouit bien que de ce qu’on partage.

  12. #12
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Points : 13 092
    Points
    13 092
    Par défaut
    non, en fait ce n'est ni faux, ni illogique. ça correspond juste à une certaine vérité avec une certaine logique

    COALESCE n'est ni plus ni moins qu'une forme abrégée de CASE (au même titre que NULLIF) :
    COALESCE(A,B,C,...) est un équivalent de CASE WHEN A IS NOT NULL THEN A ELSE COALESCE(b,c,...) END.

    dés lors, le comportement devient en effet logique, au même titre que SELECT NEWID(), NEWID() renvoie deux valeurs différentes.

    D'ailleurs, on n'obtiendra plus de NULL si l'on écrit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT COALESCE(val, 0)
    FROM (SELECT dbo.udf_test(NEWID()) AS val) T
    Dans ce cas en effet, la fonction ne sera appelée qu'une fois pour la table dérivée.

  13. #13
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Points : 13 092
    Points
    13 092
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    Donc il faut au moins un argument littéral non null, mais si c'est une variable (ou une colonne, j'imagine) ça passe sans problème avec que des null...
    Très étrange...

    Ah, ou alors j'ai compris ! C'est une histoire de type !
    Non, là aussi c'est une histoire de norme.
    Comme COALESCE est en fait un CASE, il n'est pas admis de ne spécifier que des NULL comme résultat. COALESCE(NULL,NULL) est donc rejeté au même titre que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT 
    	CASE 
    		WHEN 1 = 2 THEN NULL
    		WHEN 1 = 1 THEN NULL
    	END

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème boucle avec plusieurs conditions
    Par Naxence dans le forum Général VBA
    Réponses: 2
    Dernier message: 29/03/2012, 14h38
  2. [AC-2010] Problème critère avec plusieurs conditions
    Par ZoliveR dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 06/05/2011, 20h52
  3. [XL-2003] boucle For avec multiple conditions d'arret
    Par yvespi dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 26/05/2010, 09h30
  4. Réponses: 10
    Dernier message: 26/03/2008, 15h00
  5. Problème requête avec 2 conditions
    Par omgirl dans le forum VB 6 et antérieur
    Réponses: 5
    Dernier message: 12/07/2006, 16h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo