473 votes

Comment éviter l'erreur "diviser par zéro" en SQL ?

J'ai ce message d'erreur :

Msg 8134, Niveau 16, État 1, Ligne 1 Erreur de division par zéro rencontrée.

Quelle est la meilleure façon d'écrire le code SQL pour que je ne voie plus jamais ce message d'erreur ?

Je pourrais faire l'une ou l'autre des choses suivantes :

  • Ajouter une clause where pour que mon diviseur ne soit jamais zéro.

Ou

  • Je pourrais ajouter une déclaration de cas, afin qu'il y ait un traitement spécial pour le zéro.

Est-ce que la meilleure façon d'utiliser un NULLIF clause ?

Existe-t-il un meilleur moyen, ou comment le faire respecter ?

8 votes

Peut-être qu'une validation des données est nécessaire.

802voto

Henrik Staun Poulsen Points 2565

Afin d'éviter une erreur de "division par zéro", nous l'avons programmé comme suit :

Select Case when divisor=0 then null
Else dividend / divisor
End ,,,

Mais voici une façon beaucoup plus agréable de le faire :

Select dividend / NULLIF(divisor, 0) ...

Maintenant, le seul problème est de se souvenir du bit NullIf, si j'utilise la touche "/".

19 votes

Une façon beaucoup plus agréable de le faire "Select dividend / nullif(divisor, 0) ..." s'interrompt si le divisor est NULL.

13 votes

@Anderson Ce n'est pas vrai du tout. Etes-vous sûr que vous n'avez pas accidentellement utilisé IsNull au lieu de NullIf ? Essayez vous-même ! SELECT Value,1/NullIf(Value,0)FROM(VALUES(0),(5.0),(NULL))x(Value); A moins que par "casse" vous vouliez dire renvoie un NULL ? Vous pouvez convertir cela en ce que vous voulez avec IsNull ou Coalesce .

3 votes

@ErikE, c'est vrai... essayez d'exécuter ... select 1/nullif(null,0) ... vous obtenez "Le type du premier argument de NULLIF ne peut pas être la constante NULL car le type du premier argument doit être connu." Traitez ce problème en utilisant "coalesce(FieldName,0)" ... par exemple select 1/nullif(coalesce(null,0),0)

223voto

Tobias Domhan Points 588

Dans le cas où vous voulez retourner un zéro, au cas où une déviation de zéro se produirait, vous pouvez utiliser :

SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable

Pour chaque diviseur qui est zéro, vous obtiendrez un zéro dans le jeu de résultats.

9 votes

Certains benchmarks révèlent que COALESCE est légèrement plus lent que ISNULL. Cependant, COALESCE fait partie des normes et est donc plus portable.

50 votes

Si quelqu'un d'autre ne comprend pas immédiatement pourquoi cela fonctionne, NULLIF(d,0) renverra NULL si d est 0. En SQL, diviser par NULL renvoie NULL. Le Coalesce remplace le NULL résultant par 0.

2 votes

Notez également qu'il renvoie 0 lorsqu'un dividende ou un diviseur sont NULL alors que par défaut il renverrait NULL.

74voto

frank Points 71

Cela semblait être la meilleure solution pour ma situation lorsque j'essayais de résoudre le problème de la division par zéro, qui se produit dans mes données.

Supposons que vous souhaitiez calculer les ratios hommes/femmes pour différents clubs scolaires, mais vous découvrez que la requête suivante échoue et émet une erreur de division par zéro lorsqu'elle tente de calculer le ratio pour le club du Seigneur des anneaux, qui ne compte aucune femme :

SELECT club_id, males, females, males/females AS ratio
  FROM school_clubs;

Vous pouvez utiliser la fonction NULLIF pour éviter la division par zéro. NULLIF compare deux expressions et renvoie null si elles sont égales ou la première expression sinon.

Réécrivez la requête comme suit :

SELECT club_id, males, females, males/NULLIF(females, 0) AS ratio
  FROM school_clubs;

Tout nombre divisé par NULL donne NULL et aucune erreur n'est générée.

7 votes

Oui, en effet, c'est BIEN MEILLEUR que cette autre réponse qui a obtenu tant de votes positifs. Dans votre solution, vous avez au moins un NULL, ce qui indique que vous ne pouvez pas fournir un résultat correct. Mais si vous convertissez le résultat de NULL en Zéro, alors vous obtenez simplement des résultats erronés et trompeurs.

10 votes

À propos, si vous voulez calculer un ratio hommes/femmes, je suggère de mieux le comparer au total, comme ceci : select males/(males+females), females/(males+females) . Vous obtiendrez ainsi la répartition en pourcentage des hommes et des femmes dans un club, par exemple 31 % d'hommes et 69 % de femmes.

53voto

Taz Points 141

Vous pouvez également le faire au début de la requête :

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

Donc si vous avez quelque chose comme 100/0 il retournera NULL. Je ne l'ai fait que pour des requêtes simples, donc je ne sais pas comment cela affectera les requêtes plus longues/complexes.

1 votes

Cela fonctionne pour moi. Dans mon cas, je dois utiliser l'opération de division dans la clause WHERE. Je suis sûr qu'il n'y a pas de diviseur zéro, car lorsque je commente WHERE, il n'y a pas de valeurs zéro dans les résultats. Mais d'une manière ou d'une autre, l'optimiseur de requêtes divise par zéro pendant le filtrage. SET ARITHABORT OFF et ANSI_WARNINGS OFF font le travail - après 2 jours de lutte avec la division par zéro dans la clause WHERE. Merci !

4 votes

C'est tellement sale, mais j'adore ça ! J'en avais besoin dans une requête qui fait de l'agrégation et l'utilisation de l'instruction CASE n'était pas une option, car je devais alors ajouter cette colonne au GROUP BY, ce qui changeait totalement les résultats. Faire de la requête initiale un sous-sélection et ensuite faire un GROUP BY sur la requête externe change aussi les résultats parce qu'il y a une division impliquée.

1 votes

Bon, j'aime toujours cette "solution", mais comme beaucoup d'entre vous, j'ai pensé qu'il devait y avoir un moyen plus "propre". Et si j'oubliais de réactiver les avertissements ? Ou si quelqu'un avait masqué mon code (cela n'arrive jamais, n'est-ce pas ?) et n'avait pas pensé aux avertissements ? Quoi qu'il en soit, j'ai vu d'autres réponses concernant NULLIF(). Je connaissais NULLIF() mais je n'avais pas réalisé que diviser par NULL retournait NULL (je pensais que ce serait une erreur). Donc... j'ai fait ce qui suit : ISNULL( (SUM(foo) / NULLIF(SUM(bar),0) ), 0) AS Avg

35voto

Beska Points 6717

EDIT : Je reçois beaucoup de votes négatifs sur ce sujet récemment... alors j'ai pensé que je devais juste ajouter une note indiquant que cette réponse a été écrite avant que la question ne subisse sa dernière modification, où renvoyer null a été mis en évidence comme une option... ce qui semble très acceptable. Une partie de ma réponse répondait à des préoccupations comme celle d'Edwardo, dans les commentaires, qui semblait préconiser de renvoyer un 0. C'est le cas contre lequel je m'élevais.

REPONSE : Je pense qu'il y a un problème sous-jacent ici, à savoir que la division par 0 n'est pas légale. C'est une indication que quelque chose est fondamentalement faux. Si vous divisez par zéro, vous essayez de faire quelque chose qui n'a pas de sens mathématiquement, donc aucune réponse numérique que vous pouvez obtenir ne sera valide. (L'utilisation de null dans ce cas est raisonnable, car ce n'est pas une valeur qui sera utilisée dans des calculs mathématiques ultérieurs).

Edwardo pose donc la question suivante dans les commentaires : "Que se passe-t-il si l'utilisateur inscrit un 0 ?", et il estime qu'il devrait être acceptable d'obtenir un 0 en retour. Si l'utilisateur met zéro dans le montant, et que vous voulez que 0 soit retourné lorsqu'il le fait, alors vous devriez mettre un code au niveau des règles de gestion pour attraper cette valeur et retourner 0... et non pas avoir un cas spécial où la division par 0 = 0.

C'est une différence subtile, mais elle est importante... parce que la prochaine fois que quelqu'un appelle votre fonction et s'attend à ce qu'elle fasse la bonne chose, et qu'elle fait quelque chose de funky qui n'est pas mathématiquement correct, mais qui gère juste le cas particulier, elle a de bonnes chances de mordre quelqu'un plus tard. Vous n'êtes pas vraiment en train de diviser par 0... vous ne faites que renvoyer une mauvaise réponse à une mauvaise question.

Imaginez que je suis en train de coder quelque chose, et que je me plante. Je devrais lire une valeur d'échelle de mesure de radiation, mais dans un cas étrange que je n'ai pas anticipé, je lis 0. Je dépose ensuite ma valeur dans votre fonction... vous me renvoyez 0 ! Hourra, pas de radiation ! Sauf qu'il est vraiment là et que c'est juste que je passais une mauvaise valeur... mais je n'en ai aucune idée. Je veux que la division lance l'erreur parce que c'est le drapeau qui indique que quelque chose ne va pas.

15 votes

Je ne suis pas d'accord. Vos règles de gestion ne devraient jamais se retrouver à faire des calculs illégaux. Si vous vous retrouvez à faire quelque chose comme ça, il est fort probable que votre modèle de données soit erroné. Chaque fois que vous rencontrez une division par 0, vous devriez vous demander si la donnée n'aurait pas dû être NULL au lieu de 0.

33 votes

Je n'arrive pas à croire que j'ai été rétrogradé par quelqu'un qui m'a demandé si j'avais déjà "fait de la vraie programmation ?" parce que je dis de le faire correctement, plutôt que d'être paresseux. soupir

11 votes

Je suis désolé, je ne voulais pas vous offenser. Mais la question est parfaitement valable dans de nombreuses applications courantes de la LOB, et y répondre par un "la division par 0 n'est pas légale" n'apporte aucune valeur ajoutée, à mon avis.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X