it-swarm.com.de

Wie vermeide ich den Fehler "Division durch Null" in SQL?

Ich habe diese Fehlermeldung:

Meldung 8134, Ebene 16, Status 1, Zeile 1 Division durch aufgetretenen Nullfehler.

Was ist der beste Weg, um SQL-Code zu schreiben, damit ich diese Fehlermeldung nie mehr sehe?

Ich könnte folgendes tun:

  • Fügen Sie eine where-Klausel hinzu, damit mein Divisor niemals Null ist

Oder

  • Ich könnte eine Fallerklärung hinzufügen, so dass es eine spezielle Behandlung für Null gibt. 

Ist die beste Möglichkeit, eine NULLIF-Klausel zu verwenden? 

Gibt es einen besseren Weg oder wie kann dies durchgesetzt werden?

288

Um einen Fehler "Division durch Null" zu vermeiden, haben wir ihn folgendermaßen programmiert:

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

Aber hier ist eine viel schönere Methode:

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

Jetzt ist das einzige Problem, sich das NullIf-Bit zu merken, wenn ich die Taste "/" verwende.

533

Für den Fall, dass Sie eine Null zurückgeben möchten, können Sie Folgendes verwenden:

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

Für jeden Divisor, der Null ist, erhalten Sie eine Null in der Ergebnismenge.

150
Tobias Domhan

Dies schien die beste Lösung für meine Situation zu sein, wenn versucht wurde, die Division durch Null zu behandeln, was in meinen Daten der Fall ist.

Angenommen, Sie möchten die Verhältnisse von Männern und Frauen für verschiedene Schulklubs berechnen, entdecken jedoch, dass die folgende Abfrage fehlschlägt, und gibt einen Fehler durch Null aus, wenn versucht wird, das Verhältnis für den Lord of the Rings Club zu berechnen, der keine Frauen hat : 

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

Mit der Funktion NULLIF können Sie die Division durch Null vermeiden. NULLIF vergleicht zwei Ausdrücke und gibt null zurück, wenn sie gleich sind oder den ersten Ausdruck ansonsten. 

Schreiben Sie die Abfrage neu: 

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

Eine durch NULL dividierte Zahl ergibt NULL und es wird kein Fehler generiert.

58
frank

Sie können dies auch am Anfang der Abfrage tun:

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

Wenn Sie also etwas wie 100/0 haben, wird NULL zurückgegeben. Ich habe das nur für einfache Abfragen gemacht, daher weiß ich nicht, wie sich längere/komplexe auswirken.

39
Taz

EDIT: Ich bekomme in letzter Zeit eine Menge Downvotes ... also dachte ich, ich würde nur eine Anmerkung hinzufügen, dass diese Antwort geschrieben wurde, bevor die Frage ihre letzte Bearbeitung war, in der die Rückgabe von null als hervorgehoben wurde Option ... was sehr akzeptabel erscheint. Einige meiner Antworten waren an Bedenken wie die von Edwardo gerichtet, in den Kommentaren, die sich für die Rückgabe einer 0 aussprachen. Dies ist der Fall, gegen den ich gerügt habe.

ANTWORT: Ich denke, hier liegt ein Grundproblem vor, nämlich dass eine Division durch 0 nicht legal ist. Es ist ein Hinweis darauf, dass etwas fundamental falsch ist. Wenn Sie durch Null dividieren, versuchen Sie etwas zu tun, das mathematisch keinen Sinn ergibt. Daher ist keine numerische Antwort gültig. (Die Verwendung von null ist in diesem Fall sinnvoll, da dies kein Wert ist, der in späteren mathematischen Berechnungen verwendet wird.).

Also fragt Edwardo in den Kommentaren "Was ist, wenn der Benutzer eine 0 eingibt?", Und er plädiert dafür, dass es okay sein sollte, eine 0 als Gegenleistung zu erhalten. Wenn der Benutzer den Betrag auf Null setzt und Sie möchten, dass 0 zurückgegeben wird, wenn Sie dies tun, sollten Sie den Code auf der Ebene der Geschäftsregeln eingeben, um diesen Wert abzufangen und 0 zurückzugeben. Es gibt keinen Sonderfall, bei dem Division durch 0 = 0 

Das ist ein subtiler Unterschied, aber es ist wichtig ... weil das nächste Mal, wenn jemand Ihre Funktion aufruft und erwartet, dass sie das Richtige tut, und es etwas unkonventionelles tut, das mathematisch nicht korrekt ist, sondern nur den bestimmten Edge-Fall behandelt, den es hat gute Chance, später jemanden zu beißen. Sie teilen sich nicht wirklich durch 0 ... Sie geben nur eine schlechte Antwort auf eine schlechte Frage zurück.

Stellen Sie sich vor, ich codiere etwas und vermassle es. Ich sollte einen Strahlungsmessungs-Skalierungswert einlesen, aber in einem seltsamen Edge-Fall, den ich nicht erwartet hatte, las ich 0. Ich lasse meinen Wert in Ihre Funktion ... Sie geben mir eine 0 zurück! Hurra, keine Strahlung! Außer dass es wirklich da ist und es nur darum ging, dass ich einen schlechten Wert hatte ... aber ich habe keine Ahnung. Ich möchte, dass die Division den Fehler wirft, weil es die Flagge ist, dass etwas nicht stimmt.

31
Beska

Sie können zumindest verhindern, dass die Abfrage mit einem Fehler bricht und NULL zurückgibt, wenn eine Division durch Null vorhanden ist:

SELECT a / NULLIF(b, 0) FROM t 

Ich würde jedochNIEMALSmit coalesce in Zero konvertieren, wie es in dieser anderen Antwort gezeigt wird, die viele positive Bewertungen erhielt. Dies ist im mathematischen Sinne völlig falsch und sogar gefährlich, da Ihre Anwendung wahrscheinlich falsche und irreführende Ergebnisse liefert. 

31
SQLGeorge
SELECT Dividend / ISNULL(NULLIF(Divisor,0), 1) AS Result from table

Wenn Sie die Null mit einem Nullif () abfangen, können Sie die Division durch einen Null-Fehler () umgehen, indem Sie den Nullwert mit einem Nullwert () erfassen. 

21
Gurufaction

Das Ersetzen von "Teilen durch Null" durch Null ist umstritten - aber es ist auch nicht die einzige Option. In einigen Fällen ist das Ersetzen durch 1 (vernünftigerweise) angemessen. Ich finde mich oft mit

 ISNULL(Numerator/NULLIF(Divisor,0),1)

wenn ich mir die Verschiebungen in Punktzahlen/Zählungen ansehe, möchte ich den Standardwert 1 festlegen, wenn ich keine Daten habe. Zum Beispiel

NewScore = OldScore *  ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1) 

In den meisten Fällen habe ich dieses Verhältnis tatsächlich an anderer Stelle berechnet (nicht zuletzt, weil dies zu sehr großen Anpassungsfaktoren für kleine Nenner führen kann. In diesem Fall würde ich für OldSampleScore normalerweise einen höheren Grenzwert als einen Schwellenwert festlegen, was dann Null ausschließt Aber manchmal ist der "Hack" angebracht.

7
N Mason

Ich habe vor einiger Zeit eine Funktion geschrieben, um sie für meine gespeicherten Prozeduren zu behandeln :

print 'Creating safeDivide Stored Proc ...'
go

if exists (select * from dbo.sysobjects where  name = 'safeDivide') drop function safeDivide;
go

create function dbo.safeDivide( @Numerator decimal(38,19), @divisor decimal(39,19))
   returns decimal(38,19)
begin
 -- **************************************************************************
 --  Procedure: safeDivide()
 --     Author: Ron Savage, Central, ex: 1282
 --       Date: 06/22/2004
 --
 --  Description:
 --  This function divides the first argument by the second argument after
 --  checking for NULL or 0 divisors to avoid "divide by zero" errors.
 -- Change History:
 --
 -- Date        Init. Description
 -- 05/14/2009  RS    Updated to handle really freaking big numbers, just in
 --                   case. :-)
 -- 05/14/2009  RS    Updated to handle negative divisors.
 -- **************************************************************************
   declare @p_product    decimal(38,19);

   select @p_product = null;

   if ( @divisor is not null and @divisor <> 0 and @Numerator is not null )
      select @p_product = @Numerator / @divisor;

   return(@p_product)
end
go
5
Ron Savage
  1. Fügen Sie eine CHECK-Einschränkung hinzu, die erzwingt, dass Divisor nicht Null ist
  2. Fügen Sie dem Formular einen Prüfer hinzu, damit der Benutzer keine Nullwerte in dieses Feld eingeben kann.
3
finnw

Für Update-SQLs:

update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)
3
Vijay Bansal

Hier ist eine Situation, in der Sie durch Null teilen können. Die Geschäftsregel besagt, dass Sie zur Berechnung von Lagerumschlägen die Kosten für die für einen bestimmten Zeitraum verkauften Waren berechnen und sie annualisieren. Nachdem Sie die annualisierte Zahl erhalten haben, teilen Sie den durchschnittlichen Bestand für den Zeitraum. 

Ich betrachte die Berechnung der Anzahl von Lagerumschlägen, die in einem Zeitraum von drei Monaten auftreten. Ich habe berechnet, dass mir die Warenkosten während des dreimonatigen Zeitraums von 1.000 USD verkauft wurden. Die jährliche Umsatzrate beträgt 4.000 $ (3/3) * 12. Der Anfangsbestand ist 0. Der Endbestand ist 0. Mein durchschnittlicher Bestand ist jetzt 0. Ich habe einen Umsatz von 4000 Dollar pro Jahr und keinen Bestand. Dies ergibt eine unendliche Anzahl von Windungen. Dies bedeutet, dass mein gesamtes Inventar von Kunden konvertiert und gekauft wird. 

Dies ist eine Geschäftsregel zum Berechnen von Lagerumschlägen. 

2
Jimmy

Es gibt keine magische globale Einstellung "Division um 0 Ausnahmen ausschalten". Die Operation muss werfen, da sich die mathematische Bedeutung von x/0 von der NULL-Bedeutung unterscheidet, also kann NULL nicht zurückgegeben werden .. Ich nehme an, Sie kümmern sich um das Offensichtliche und Ihre Abfragen haben Bedingungen, die die Datensätze beseitigen sollten mit dem 0-Divisor und niemals die Division bewerten. Normalerweise erwarten die meisten Entwickler, dass sich SQL wie prozedurale Sprachen verhält und einen logischen Operator-Kurzschluss bietet, aber NICHT. Ich empfehle Ihnen, diesen Artikel zu lesen: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html

2
Remus Rusanu

Filtern Sie Daten mithilfe einer where-Klausel heraus, sodass Sie keine 0-Werte erhalten.

1
nunespascal
CREATE FUNCTION dbo.Divide(@Numerator Real, @Denominator Real)
RETURNS Real AS
/*
Purpose:      Handle Division by Zero errors
Description:  User Defined Scalar Function
Parameter(s): @Numerator and @Denominator

Test it:

SELECT 'Numerator = 0' Division, dbo.fn_CORP_Divide(0,16) Results
UNION ALL
SELECT 'Denominator = 0', dbo.fn_CORP_Divide(16,0)
UNION ALL
SELECT 'Numerator is NULL', dbo.fn_CORP_Divide(NULL,16)
UNION ALL
SELECT 'Denominator is NULL', dbo.fn_CORP_Divide(16,NULL)
UNION ALL
SELECT 'Numerator & Denominator is NULL', dbo.fn_CORP_Divide(NULL,NULL)
UNION ALL
SELECT 'Numerator & Denominator = 0', dbo.fn_CORP_Divide(0,0)
UNION ALL
SELECT '16 / 4', dbo.fn_CORP_Divide(16,4)
UNION ALL
SELECT '16 / 3', dbo.fn_CORP_Divide(16,3)

*/
BEGIN
    RETURN
        CASE WHEN @Denominator = 0 THEN
            NULL
        ELSE
            @Numerator / @Denominator
        END
END
GO
1
Gregory Hart

Manchmal ist 0 möglicherweise nicht angemessen, aber manchmal ist 1 auch nicht geeignet. Manchmal kann ein Sprung von 0 auf 100.000.000, der als 1 oder 100-prozentige Änderung bezeichnet wird, auch irreführend sein. 100.000.000 Prozent könnten in diesem Szenario angemessen sein. Es hängt davon ab, welche Schlussfolgerungen Sie basierend auf den Prozentsätzen oder Verhältnissen ziehen möchten. 

Zum Beispiel: Ein sehr klein verkaufter Artikel, der von 2-4 verkauft wird, und ein sehr groß verkaufter Artikel, der von 1.000.000 auf 2.000.000 verkauft wird, kann sehr unterschiedliche Dinge für einen Analyst oder das Management bedeuten, würde aber beide zu 100% oder 1 durchgehen Veränderung. 

Es ist möglicherweise einfacher, NULL-Werte zu isolieren, als eine Reihe von 0% oder 100% Zeilen mit legitimen Daten zu durchsuchen. Oft kann eine 0 im Nenner auf einen Fehler oder einen fehlenden Wert hinweisen, und Sie möchten möglicherweise nicht einfach einen beliebigen Wert eingeben, nur um das Daten-Set ordentlich aussehen zu lassen.

CASE
     WHEN [Denominator] = 0
     THEN NULL --or any value or sub case
     ELSE [Numerator]/[Denominator]
END as DivisionProblem
0
Joeyslaptop

Sie können den Fehler entsprechend behandeln, wenn er an das aufrufende Programm weitergegeben wird (oder ihn ignorieren, wenn Sie dies wünschen). In C # wird bei Fehlern, die in SQL auftreten, eine Ausnahme ausgelöst, die ich abfangen und dann in meinem Code behandeln kann, genau wie bei jedem anderen Fehler.

Ich stimme Beska darin zu, dass Sie den Fehler nicht verbergen möchten. Sie haben es vielleicht nicht mit einem Kernreaktor zu tun, aber Fehler zu verbergen ist im Allgemeinen eine schlechte Programmierpraxis. Dies ist einer der Gründe, warum die meisten modernen Programmiersprachen eine strukturierte Ausnahmebehandlung implementieren, um den tatsächlichen Rückgabewert mit einem Fehler-/Statuscode zu entkoppeln. Dies gilt insbesondere, wenn Sie Mathe machen. Das größte Problem ist, dass Sie nicht zwischen einer korrekt berechneten 0 als Ergebnis eines Fehlers oder einer 0 als Ergebnis eines Fehlers unterscheiden können. Stattdessen ist jeder zurückgegebene Wert der berechnete Wert, und wenn etwas schief geht, wird eine Ausnahme ausgelöst. Dies hängt natürlich davon ab, wie Sie auf die Datenbank zugreifen und welche Sprache Sie verwenden. Es sollte jedoch immer eine Fehlermeldung angezeigt werden, mit der Sie umgehen können.

try
{
    Database.ComputePercentage();
}
catch (SqlException e)
{
    // now you can handle the exception or at least log that the exception was thrown if you choose not to handle it
    // Exception Details: System.Data.SqlClient.SqlException: Divide by zero error encountered.
}
0
Despertar

Verwenden Sie NULLIF(exp,0), aber auf diese Weise - NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0) bricht ab, wenn exp null ist, aber NULLIF(ISNULL(exp,0),0) bricht nicht ab

0

So habe ich es behoben:

IIF (ValueA! = 0, Total/ValueA, 0)

Es kann in ein Update eingepackt werden:

SET Pct = IIF (ValueA! = 0, Total/ValueA, 0)

Oder in einer Auswahl:

SELECT IIF (ValueA! = 0, Total/ValueA, 0) AS Pct FROM Tablename;

Gedanken?

0
CorvetteGuru