it-swarm.asia

Bagaimana cara menghindari kesalahan "divide by zero" di SQL?

Saya punya pesan kesalahan ini:

Msg 8134, Level 16, State 1, Line 1 Divide by zero error found.

Apa cara terbaik untuk menulis kode SQL sehingga saya tidak akan pernah melihat pesan kesalahan ini lagi?

Saya dapat melakukan salah satu dari yang berikut:

  • Tambahkan klausa dimana sehingga pembagi saya tidak pernah nol

Atau

  • Saya bisa menambahkan pernyataan kasus, sehingga ada perlakuan khusus untuk nol. 

Apakah cara terbaik untuk menggunakan klausa NULLIF

Apakah ada cara yang lebih baik, atau bagaimana ini bisa ditegakkan?

288

Untuk menghindari kesalahan "Division by zero", kami telah memprogramnya seperti ini:

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

Tapi di sini ada cara yang lebih baik untuk melakukannya:

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

Sekarang satu-satunya masalah adalah untuk mengingat bit NullIf, jika saya menggunakan kunci "/".

533

Jika Anda ingin mengembalikan nol, jika terjadi nol divisi, Anda dapat menggunakan:

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

Untuk setiap pembagi yang nol, Anda akan mendapatkan nol di set hasil.

150
Tobias Domhan

Ini tampaknya menjadi perbaikan terbaik untuk situasi saya ketika mencoba mengatasi pembagian dengan nol, yang memang terjadi pada data saya.

Misalkan Anda ingin menghitung rasio pria-wanita untuk berbagai klub sekolah, tetapi Anda menemukan bahwa kueri berikut gagal dan mengeluarkan kesalahan dibagi-oleh-nol ketika mencoba menghitung rasio untuk Klub Lord of the Rings, yang tidak memiliki wanita : 

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

Anda dapat menggunakan fungsi NULLIF untuk menghindari pembagian dengan nol. NULLIF membandingkan dua ekspresi dan mengembalikan null jika mereka sama atau ekspresi pertama sebaliknya. 

Tulis ulang kueri sebagai: 

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

Setiap angka dibagi dengan NULL memberikan NULL, dan tidak ada kesalahan yang dihasilkan.

58
frank

Anda juga dapat melakukan ini di awal kueri:

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

Jadi jika Anda memiliki sesuatu seperti 100/0 itu akan mengembalikan NULL. Saya hanya melakukan ini untuk pertanyaan sederhana, jadi saya tidak tahu bagaimana itu akan mempengaruhi yang lebih lama/kompleks.

39
Taz

EDIT: Saya mendapat banyak downvotes tentang ini baru-baru ini ... jadi saya pikir saya hanya akan menambahkan catatan bahwa jawaban ini ditulis sebelum pertanyaan menjalani edit terakhir, di mana pengembalian null disorot sebagai opsi ... yang tampaknya sangat dapat diterima. Beberapa jawaban saya ditujukan kepada keprihatinan seperti itu dari Edwardo, dalam komentar, yang tampaknya menganjurkan pengembalian 0. Ini adalah kasus saya menentang.

JAWABAN: Saya pikir ada masalah mendasar di sini, yaitu pembagian dengan 0 tidak legal. Ini indikasi bahwa ada sesuatu yang secara fundamental salah. Jika Anda membaginya dengan nol, Anda mencoba melakukan sesuatu yang tidak masuk akal secara matematis, sehingga tidak ada jawaban numerik yang Anda dapatkan akan valid. (Penggunaan nol dalam kasus ini masuk akal, karena ini bukan nilai yang akan digunakan dalam perhitungan matematika nanti).

Jadi Edwardo bertanya di komentar "bagaimana jika pengguna memasukkan 0?", Dan dia menganjurkan bahwa tidak apa-apa untuk mendapatkan 0 sebagai balasan. Jika pengguna memasukkan nol dalam jumlah, dan Anda ingin 0 dikembalikan ketika mereka melakukan itu, maka Anda harus memasukkan kode pada tingkat aturan bisnis untuk menangkap nilai itu dan mengembalikan 0 ... tidak memiliki kasus khusus di mana pembagian dengan 0 = 0. 

Itu perbedaan yang halus, tetapi ini penting ... karena saat seseorang memanggil fungsi Anda dan mengharapkannya untuk melakukan hal yang benar, dan itu melakukan sesuatu yang funky yang tidak benar secara matematis, tetapi hanya menangani kasus Edge tertentu itu punya peluang bagus untuk menggigit seseorang nanti. Anda tidak benar-benar membaginya dengan 0 ... Anda hanya mengembalikan jawaban yang buruk ke pertanyaan yang buruk.

Bayangkan saya sedang mengkode sesuatu, dan saya mengacaukannya. Saya seharusnya membaca nilai skala pengukuran radiasi, tetapi dalam kasus Edge yang aneh saya tidak mengantisipasi, saya membaca 0. Saya kemudian menjatuhkan nilai saya ke dalam fungsi Anda ... Anda mengembalikan saya 0! Hore, tidak ada radiasi! Kecuali itu benar-benar ada dan hanya saja saya melewati nilai buruk ... tapi saya tidak tahu. Saya ingin divisi melempar kesalahan karena itu adalah bendera bahwa ada sesuatu yang salah.

31
Beska

Anda setidaknya dapat menghentikan permintaan agar tidak putus dengan kesalahan dan mengembalikan NULL jika ada pembagian dengan nol:

SELECT a / NULLIF(b, 0) FROM t 

Namun, saya akanTIDAK PERNAHmengonversikannya ke Zero dengan coalesce seperti yang ditunjukkan pada jawaban lain yang mendapat banyak upvotes. Ini sepenuhnya salah dalam arti matematis, dan bahkan berbahaya karena aplikasi Anda kemungkinan akan mengembalikan hasil yang salah dan menyesatkan. 

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

Dengan menangkap nol dengan nullif (), maka null yang dihasilkan dengan isnull () Anda dapat menghindari pembagian Anda dengan kesalahan nol. 

21
Gurufaction

Mengganti "bagi dengan nol" dengan nol adalah kontroversial - tetapi itu juga bukan satu-satunya pilihan. Dalam beberapa kasus, mengganti dengan 1 sesuai (wajar). Saya sering menemukan diri saya menggunakan

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

ketika saya melihat pergeseran dalam skor/jumlah, dan ingin default ke 1 jika saya tidak memiliki data. Sebagai contoh

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

Lebih sering daripada tidak, saya sudah benar-benar menghitung rasio ini di tempat lain (paling tidak karena dapat melempar beberapa faktor penyesuaian yang sangat besar untuk penyebut rendah. Dalam hal ini saya biasanya mengontrol OldSampleScore lebih besar dari ambang; yang kemudian menghalangi nol Tapi terkadang 'retasan' itu sesuai.

7
N Mason

Saya menulis sebuah fungsi beberapa waktu lalu untuk menanganinya untuk prosedur tersimpan :

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. Tambahkan batasan PERIKSA yang memaksa Divisor menjadi bukan nol
  2. Tambahkan validator ke formulir sehingga pengguna tidak bisa memasukkan nilai nol ke dalam bidang ini.
3
finnw

Untuk memperbarui SQL:

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

Berikut adalah situasi di mana Anda dapat membaginya dengan nol. Aturan bisnis adalah bahwa untuk menghitung perputaran persediaan, Anda mengambil harga pokok penjualan untuk suatu periode, menjadikannya tahunan. Setelah Anda memiliki angka tahunan, Anda membagi dengan persediaan rata-rata untuk periode tersebut. 

Saya sedang menghitung jumlah perputaran persediaan yang terjadi dalam periode tiga bulan. Saya telah menghitung bahwa saya memiliki Harga Pokok Penjualan selama periode tiga bulan sebesar $ 1.000. Tingkat penjualan tahunan adalah $ 4.000 ($ 1.000/3) * 12. Persediaan awal adalah 0. Persediaan akhir adalah 0. Persediaan rata-rata saya sekarang adalah 0. Saya memiliki penjualan $ 4000 per tahun, dan tidak ada persediaan. Ini menghasilkan jumlah belokan yang tak terbatas. Ini berarti bahwa semua inventaris saya sedang dikonversi dan dibeli oleh pelanggan. 

Ini adalah aturan bisnis tentang cara menghitung perputaran persediaan. 

2
Jimmy

Tidak ada pengaturan global ajaib 'turn Division by 0 exception off'. Operasi harus membuang, karena arti matematika dari x/0 berbeda dari makna NULL, sehingga tidak dapat mengembalikan NULL . Saya berasumsi Anda mengurus yang jelas dan pertanyaan Anda memiliki kondisi yang harus menghilangkan catatan dengan 0 pembagi dan tidak pernah mengevaluasi pembagian. 'Gotcha' yang biasa adalah daripada kebanyakan pengembang mengharapkan SQL berperilaku seperti bahasa prosedural dan menawarkan hubungan pendek operator logis, tetapi ia BUKAN. Saya sarankan Anda membaca artikel ini: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html

2
Remus Rusanu

Memfilter data menggunakan klausa dimana sehingga Anda tidak mendapatkan nilai 0.

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

Terkadang, 0 mungkin tidak sesuai, tetapi terkadang 1 juga tidak tepat. Kadang-kadang lompatan dari 0 hingga 100.000.000 digambarkan sebagai perubahan 1 atau 100 persen juga bisa menyesatkan. 100.000.000 persen mungkin sesuai dalam skenario itu. Itu tergantung pada jenis kesimpulan yang ingin Anda gambar berdasarkan persentase atau rasio. 

Misalnya, barang yang sangat kecil yang dijual dari 2-4 yang terjual dan barang yang sangat besar berubah dari 1.000.000 menjadi 2.000.000 yang terjual mungkin berarti hal yang sangat berbeda bagi analis atau manajemen, tetapi keduanya akan menjadi 100% atau 1 perubahan. 

Mungkin lebih mudah untuk mengisolasi nilai NULL daripada menjelajahi sekitar 0% atau 100% baris yang dicampur dengan data yang sah. Seringkali, 0 dalam penyebut dapat menunjukkan kesalahan atau nilai yang hilang, dan Anda mungkin tidak ingin hanya mengisi nilai arbitrer hanya untuk membuat dataset Anda terlihat rapi.

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

Anda dapat menangani kesalahan dengan tepat ketika merambat kembali ke program panggilan (atau mengabaikannya jika itu yang Anda inginkan). Dalam C # setiap kesalahan yang terjadi di SQL akan membuang pengecualian yang bisa saya tangkap dan kemudian menangani kode saya, sama seperti kesalahan lainnya.

Saya setuju dengan Beska karena Anda tidak ingin menyembunyikan kesalahan. Anda mungkin tidak berurusan dengan reaktor nuklir tetapi menyembunyikan kesalahan secara umum adalah praktik pemrograman yang buruk. Ini adalah salah satu alasan kebanyakan bahasa pemrograman modern menerapkan penanganan pengecualian terstruktur untuk memisahkan nilai pengembalian aktual dengan kode kesalahan/status. Ini terutama benar ketika Anda melakukan matematika. Masalah terbesar adalah bahwa Anda tidak dapat membedakan antara 0 yang dikembalikan dengan benar atau 0 sebagai hasil dari kesalahan. Sebaliknya nilai yang dikembalikan adalah nilai yang dihitung dan jika terjadi kesalahan pengecualian dilemparkan. Ini tentu saja akan berbeda tergantung pada bagaimana Anda mengakses database dan bahasa apa yang Anda gunakan tetapi Anda harus selalu bisa mendapatkan pesan kesalahan yang bisa Anda tangani.

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

Gunakan NULLIF(exp,0) tetapi dengan cara ini - NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0) rusak jika exp null tetapi NULLIF(ISNULL(exp,0),0) tidak akan pecah

0
Johnny Kancharla