วิธีการหลีกเลี่ยงข้อผิดพลาด "หารด้วยศูนย์" ใน SQL?


363

ฉันมีข้อผิดพลาดนี้:

ข่าวสารเกี่ยวกับ 8134 ระดับ 16 สถานะ 1 เส้น 1 หารด้วยข้อผิดพลาดที่พบ

เป็นวิธีที่ดีที่สุดในการเขียนรหัส SQL เพื่อที่ฉันจะไม่เห็นข้อความแสดงข้อผิดพลาดนี้อีกครั้งคืออะไร

ฉันสามารถทำอย่างใดอย่างหนึ่งต่อไปนี้:

  • เพิ่มส่วนคำสั่ง where เพื่อให้ตัวหารไม่เป็นศูนย์

หรือ

  • ฉันสามารถเพิ่มคำสั่งกรณีเพื่อให้มีการดูแลเป็นพิเศษสำหรับศูนย์

เป็นวิธีที่ดีที่สุดในการใช้NULLIFประโยค?

มีวิธีที่ดีกว่าหรือจะบังคับใช้วิธีนี้ได้อย่างไร


7
บางทีการตรวจสอบความถูกต้องของข้อมูลบางอย่างนั้นเป็นไปตามลำดับ
Anthony

คำตอบ:


644

เพื่อหลีกเลี่ยงข้อผิดพลาด "หารด้วยศูนย์" เราได้ตั้งโปรแกรมไว้ดังนี้:

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

แต่นี่เป็นวิธีที่ดีกว่าในการทำ:

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

ตอนนี้ปัญหาเดียวคือการจดจำบิต Null ถ้าฉันใช้คีย์ "/"


13
วิธีที่ดีกว่าในการทำ "แบ่งเงินปันผล / nullif (ตัวหาร, 0) ... " แบ่งถ้าตัวหารเป็น NULL
Anderson

8
@Anderson มันไม่จริงเลย คุณแน่ใจว่าคุณไม่ได้ตั้งใจใช้IsNullแทนNullIf? ลองด้วยตัวเอง! SELECT Value,1/NullIf(Value,0)FROM(VALUES(0),(5.0),(NULL))x(Value);ถ้า "หยุด" คุณหมายถึงคืนค่า NULL หรือไม่ คุณสามารถแปลงว่าสิ่งที่คุณต้องการด้วยหรือIsNull Coalesce
ErikE

1
@ErikE เป็นจริง ... ลองเรียกใช้ ... เลือก 1 / nullif (null, 0) ... คุณจะได้รับ "ประเภทของอาร์กิวเมนต์แรกเป็น NULLIF ไม่สามารถเป็นค่าคงที่ NULL ได้เนื่องจากชนิดของอาร์กิวเมนต์แรกมี เป็นที่รู้จัก " จัดการสิ่งนี้โดยใช้ "coalesce (FieldName, 0)" ... เช่นเลือก 1 / nullif (coalesce (null, 0), 0)
John Joseph

1
@JohnJoseph ฉันไม่สามารถบอกได้ว่าคุณเห็นด้วยกับฉันหรือเถียงกับฉัน
ErikE

1
@JohnJoseph ลองดูที่ข้อผิดพลาดที่คุณมี ใช่SELECT 1 / NULLIF(NULL, 0)ล้มเหลว แต่เป็นเพราะNULLIF()ต้องการทราบประเภทข้อมูลของอาร์กิวเมนต์แรก ตัวอย่างที่เปลี่ยนแปลงนี้ใช้งานได้ดี: SELECT 1 / NULLIF(CAST(NULL AS INT), 0). ในชีวิตจริงคุณกำลังจะจัดทำคอลัมน์ตารางNULLIF()แทนที่จะเป็นNULLค่าคงที่ SELECT 1 / NULLIF(SomeNullableColumn, 0) FROM SomeTableตั้งแต่คอลัมน์ของตารางมีประเภทข้อมูลที่รู้จักกันนี้ยังทำงานได้ดี:
MarredCheese

180

ในกรณีที่คุณต้องการคืนค่าศูนย์ในกรณีที่มีค่าศูนย์เบี่ยงเบนเกิดขึ้นคุณสามารถใช้:

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

สำหรับตัวหารทั้งหมดที่เป็นศูนย์คุณจะได้รับศูนย์ในชุดผลลัพธ์


9
เกณฑ์มาตรฐานบางอย่างแสดงให้เห็นว่า COALESCE นั้นช้ากว่า ISNULL เล็กน้อย อย่างไรก็ตาม COALESCE อยู่ในมาตรฐานจึงพกพาได้มากกว่า
Paul Chernoch

37
ถ้าคนอื่นไม่เข้าใจว่าทำไมสิ่งนี้ถึงได้ผลทันที NULLIF (d, 0) จะคืนค่า NULL ถ้า d เป็น 0 ใน SQL การหารด้วย NULL จะส่งกลับค่า NULL The Coalesce จะแทนที่ NULL ที่เป็นผลลัพธ์โดย 0
GuiSim

16
@SQLGeorge ในขณะที่ฉันเห็นด้วยกับข้อโต้แย้งของคุณโปรดทราบว่ามีบางกรณีที่เราใส่ใจในสิ่งที่ถูกต้องมากกว่าสถิติที่ถูกต้องทางคณิตศาสตร์ ในบางกรณีเมื่อใช้ฟังก์ชันสถิติ 0 หรือแม้แต่ 1 เป็นผลลัพธ์ที่ยอมรับได้เมื่อตัวหารเป็นศูนย์
Athafoud

10
ใครสามารถอธิบายให้ฉันฟังได้ว่าทำไมสิ่งนี้ถึงไม่ดี? ถ้าฉันพยายามหาเปอร์เซ็นต์และตัวหารเป็นศูนย์ฉันก็อยากให้ผลลัพธ์เป็นศูนย์มากที่สุด
ทอดด์คม

10
ฉันคิดว่า @George และ @ James / Wilson เข้าใจผิดโดยพื้นฐานแล้วคำถามที่ถูกถาม มีแอปพลิเคชั่นธุรกิจที่แน่นอนว่าการส่งคืน "0" เหมาะสมแม้ว่ามันจะไม่เป็นความจริงทางเทคนิคจากมุมมองทางคณิตศาสตร์
Sean Branchaw

66

นี่ดูเหมือนจะเป็นการแก้ไขที่ดีที่สุดสำหรับสถานการณ์ของฉันเมื่อพยายามหาที่อยู่หารด้วยศูนย์ซึ่งเกิดขึ้นในข้อมูลของฉัน

สมมติว่าคุณต้องการคำนวณอัตราส่วนชาย - หญิงสำหรับสโมสรในโรงเรียนต่าง ๆ แต่คุณค้นพบว่าแบบสอบถามต่อไปนี้ล้มเหลวและมีข้อผิดพลาดแบบแบ่งศูนย์เมื่อพยายามคำนวณอัตราส่วนสำหรับลอร์ดออฟเดอะริงส์ซึ่งไม่มีผู้หญิง :

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

คุณสามารถใช้ฟังก์ชั่นNULLIFเพื่อหลีกเลี่ยงการหารด้วยศูนย์ NULLIFเปรียบเทียบสองนิพจน์และคืนค่าว่างถ้าพวกเขาเท่ากันหรือนิพจน์แรก

เขียนคำสืบค้นใหม่เป็น:

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

จำนวนใด ๆ ที่หารด้วยการNULLให้NULLและไม่มีการสร้างข้อผิดพลาด


6
ใช่แน่นอนว่าเป็นวิธีที่ดีกว่าว่าคำตอบอื่น ๆ ที่ได้มีจำนวนมากดังนั้น upvotes ในโซลูชันของคุณคุณมี NULL เป็นอย่างน้อยซึ่งบ่งชี้ว่าคุณไม่สามารถให้ผลลัพธ์ที่ถูกต้องได้ แต่ถ้าคุณแปลงผลลัพธ์จาก NULL เป็นศูนย์คุณก็จะได้ผลลัพธ์ที่ผิดและทำให้เข้าใจผิด
SQL Police

8
โดยวิธีการที่ถ้าคุณต้องการที่จะคำนวณชาย / select males/(males+females), females/(males+females)หญิงอัตราส่วนแล้วฉันขอแนะนำให้ดีขึ้นเปรียบเทียบกับจำนวนที่เสนอเช่นนี้ นี่จะให้เปอร์เซ็นต์การกระจายตัวของชายและหญิงในคลับเช่นผู้ชาย 31% ผู้หญิง 69%
SQL Police

44

คุณสามารถทำสิ่งนี้ได้ที่จุดเริ่มต้นของแบบสอบถาม:

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

ดังนั้นถ้าคุณมีบางอย่าง100/0มันจะคืนค่า NULL ฉันทำสิ่งนี้เพื่อการสืบค้นง่าย ๆ เท่านั้นดังนั้นฉันจึงไม่รู้ว่ามันจะมีผลกระทบกับคำถามที่ซับซ้อนอีกต่อไปหรือไม่


1
ได้ผลสำหรับฉัน ในกรณีของฉันฉันต้องใช้การดำเนินการหารที่คำสั่ง WHERE ฉันแน่ใจว่าไม่มีตัวคั่นศูนย์เพราะเมื่อฉันแสดงความคิดเห็นว่าที่ใดจะไม่มีค่าศูนย์ที่ผลลัพธ์ แต่เครื่องมือเพิ่มประสิทธิภาพข้อความค้นหาจะหารด้วยศูนย์ในขณะที่กรอง SET ARITHABORT OFF SET และ ANSI_WARNINGS OFF ทำงาน - หลังจาก 2 วันของการต่อสู้โดยแบ่งเป็นศูนย์ที่ส่วนคำสั่ง WHERE ขอบคุณ!
huhu78

2
"รู้สึก" สกปรกมาก แต่ฉันชอบมันมาก! จำเป็นต้องใช้ในแบบสอบถามที่ทำการรวมและใช้คำสั่ง CASE ไม่ใช่ตัวเลือกเพราะฉันต้องเพิ่มคอลัมน์นั้นใน GROUP BY ซึ่งเปลี่ยนผลลัพธ์โดยสิ้นเชิง การทำให้แบบสอบถามย่อยเริ่มต้นจากนั้นเลือก GROUP BY ในแบบสอบถามภายนอกจะเปลี่ยนผลลัพธ์เนื่องจากมีการหารเกี่ยวข้อง
Andrew Steitz

1
ตกลงดังนั้นฉันยังคงชอบ "วิธีแก้ปัญหา" นี้ แต่เหมือนคุณหลายคนอาจรู้สึกว่าฉันรู้สึกว่าต้องมีวิธี "สะอาด" ถ้าฉันลืมเปิดใช้งานคำเตือนอีกครั้ง หรือมีคนอุดรหัสของฉัน (ที่ไม่เคยเกิดขึ้นใช่มั้ย?) และไม่ได้คิดเกี่ยวกับคำเตือน? อย่างไรก็ตามเห็นคำตอบอื่น ๆ เกี่ยวกับ NULLIF () ฉันรู้เกี่ยวกับ NULLIF () แต่ไม่ทราบว่าการหารด้วย NULL จะส่งกลับค่า NULL (ฉันคิดว่ามันจะเป็นข้อผิดพลาด) ดังนั้น ... ฉันไปกับสิ่งต่อไปนี้: ISNULL ((SUM (foo) / NULLIF (SUM (บาร์), 0)), 0) AS ค่าเฉลี่ย
Andrew Steitz

2
ฉันไม่ทราบวิธีแก้ปัญหานี้ ฉันไม่แน่ใจว่าฉันชอบมัน แต่มันอาจจะมีประโยชน์ที่จะรู้ว่าบางวัน ขอบคุณมาก.
Henrik Staun Poulsen

1
นี่เป็นวิธีที่ง่ายที่สุด แต่โปรดทราบว่าจะทำให้ประสิทธิภาพลดลง จากdocs.microsoft.com/en-us/sql/t-sql/statements/… : "การตั้งค่า ARITHABORT เป็น OFF อาจส่งผลเสียต่อการเพิ่มประสิทธิภาพการสืบค้นที่นำไปสู่ปัญหาด้านประสิทธิภาพ"
mono blaine

36

อย่างน้อยคุณสามารถหยุดคิวรีไม่ให้เกิดข้อผิดพลาดและส่งคืนNULLหากมีการหารด้วยศูนย์:

SELECT a / NULLIF(b, 0) FROM t 

อย่างไรก็ตามฉันจะไม่แปลงเป็นศูนย์ด้วยcoalesceเหมือนว่ามันจะปรากฏในคำตอบอื่น ๆ ที่มี upvotes มากมาย นี่เป็นความผิดทางคณิตศาสตร์อย่างสมบูรณ์และเป็นอันตรายแม้ใบสมัครของคุณอาจส่งคืนผลลัพธ์ที่ผิดและทำให้เข้าใจผิด


32

แก้ไข: ฉันได้รับ downvotes มากเมื่อเร็ว ๆ นี้ ... ดังนั้นฉันคิดว่าฉันเพิ่งเพิ่มบันทึกว่าคำตอบนี้ถูกเขียนก่อนที่คำถามจะได้รับการแก้ไขล่าสุดโดยที่การคืนค่า null ถูกเน้นเป็นตัวเลือก .. . ซึ่งดูเหมือนว่ายอมรับได้มาก บางคำตอบของฉันถูกส่งไปยังข้อกังวลเช่นเดียวกับของ Edwardo ในความคิดเห็นที่ดูเหมือนจะสนับสนุนการกลับ 0 นี่คือกรณีที่ฉันกำลังค้านกับ

คำตอบ: ฉันคิดว่ามีปัญหาพื้นฐานที่นี่ซึ่งการหารด้วย 0 นั้นไม่ถูกกฎหมาย เป็นข้อบ่งชี้ว่ามีบางอย่างผิดปกติ หากคุณหารด้วยศูนย์คุณกำลังพยายามทำสิ่งที่ไม่สมเหตุสมผลทางคณิตศาสตร์ดังนั้นจึงไม่มีคำตอบที่เป็นตัวเลขที่คุณจะได้รับ (การใช้ค่า null ในกรณีนี้สมเหตุสมผลเนื่องจากไม่ใช่ค่าที่จะใช้ในการคำนวณทางคณิตศาสตร์ในภายหลัง)

ดังนั้น Edwardo จึงถามความเห็นว่า "จะเกิดอะไรขึ้นถ้าผู้ใช้ใส่ 0?" และเขาก็สนับสนุนว่าควรจะได้รับ 0 เป็นการตอบแทน หากผู้ใช้ใส่จำนวนศูนย์และคุณต้องการให้ 0 คืนเมื่อพวกเขาทำเช่นนั้นคุณควรใส่รหัสในระดับกฎธุรกิจเพื่อรับค่านั้นและส่งคืน 0 ... ไม่มีกรณีพิเศษบางอย่างที่หารด้วย 0 = 0

นั่นเป็นความแตกต่างเล็กน้อย แต่สำคัญ ... เพราะครั้งต่อไปที่มีคนเรียกฟังก์ชั่นของคุณและคาดว่ามันจะทำสิ่งที่ถูกต้องและมันจะทำอะไรที่ขี้ขลาดที่ไม่ถูกต้องทางคณิตศาสตร์ โอกาสที่ดีที่จะกัดคนในภายหลัง คุณไม่ได้หารด้วย 0 ... คุณแค่ส่งคืนคำตอบที่ไม่ดีสำหรับคำถามที่ไม่ดี

ลองนึกภาพฉันกำลังเขียนโค้ดบางอย่างและทำให้ฉันพลาด ฉันควรอ่านค่าสเกลของการวัดรังสี แต่ในกรณีที่แปลก ๆ ที่ฉันไม่คาดคิดฉันอ่านเป็น 0 จากนั้นส่งค่าของฉันไปยังฟังก์ชั่นของคุณ ... คุณคืน 0 ให้ฉัน! Hurray ไม่มีรังสี! ยกเว้นมันอยู่ที่นั่นจริง ๆ และมันก็แค่ว่าฉันกำลังผ่านคุณค่าที่ไม่ดี ... แต่ฉันก็ไม่รู้ ฉันต้องการหารเพื่อโยนข้อผิดพลาดเพราะมันเป็นธงที่บางสิ่งผิด


15
ฉันไม่เห็นด้วย. กฎเกณฑ์ทางธุรกิจของคุณไม่ควรจบลงด้วยการทำคณิตศาสตร์ที่ผิดกฎหมาย หากคุณลงเอยด้วยการทำสิ่งนี้น่าจะเป็นไปได้มากที่ตัวแบบข้อมูลของคุณจะผิด เมื่อใดก็ตามที่คุณพบหารด้วย 0 คุณควรไตร่ตรองว่าข้อมูลควรเป็น NULL แทนที่จะเป็น 0
Remus Rusanu

32
ฉันไม่สามารถเชื่อว่าฉันถูก downvoted โดยคนที่ถามว่าฉันเคย "ทำการเขียนโปรแกรมจริง ๆ ?" เพราะฉันพูดว่าทำถูกต้องแทนที่จะเป็นคนขี้เกียจ ถอนหายใจ
Beska

11
ฉันขอโทษฉันไม่ได้ตั้งใจจะทำร้ายคุณ แต่คำถามนั้นใช้ได้อย่างสมบูรณ์ในแอปพลิเคชัน LOB ทั่วไปจำนวนมากและการตอบคำถามด้วย "การหารด้วย 0 ไม่ถูกกฎหมาย" ไม่ได้เพิ่มคุณค่า IMHO
Eduardo Molteni

2
@JackDouglas ถูกต้อง นี่เป็นกรณีที่คุณต้องการให้กฎธุรกิจจัดการกับกรณีพิเศษด้วยวิธีพิเศษ ... แต่ไม่ควรเป็นคณิตศาสตร์พื้นฐานที่ส่งคืนค่าว่าง ควรเป็นกฎทางธุรกิจ คำตอบที่ยอมรับจะคืนค่าว่างซึ่งเป็นวิธีที่ใช้ได้ในการจัดการ การโยนข้อยกเว้นก็ไม่เป็นไรเช่นกัน การระบุและจัดการก่อนที่จะไปที่ SQL จะเป็นเนื้อหาที่เหมาะสมที่สุด การให้ฟังก์ชันบางอย่างที่สิ่งอื่น ๆ สามารถโทรหาที่ส่งคืนค่าที่ไม่ถูกต้องทางคณิตศาสตร์ไม่ใช่วิธีที่จะไปเพราะกรณีพิเศษนั้นอาจไม่สามารถใช้กับผู้โทรรายอื่นเหล่านั้นได้
Beska

4
@ JackDouglas ใช่นั่นเป็นบทสรุปที่ดีที่ฉันเห็นด้วย แต่เดิมคำถามดูเหมือนจะเป็นประโยคว่า "ฉันจะทำอย่างไรเพื่อซ่อนข้อผิดพลาดนี้" ตั้งแต่นั้นมาก็มีการพัฒนา กลับมาเป็นโมฆะในที่สุดคำตอบที่เขาจะมาดูเหมือนว่าหนึ่งในเหตุผลที่เหมาะสม (ฉันขอแนะนำอย่างยิ่งว่าจะไม่ส่งคืน 0 หรือหมายเลขอื่น ๆ )
Beska

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

โดยการจับศูนย์ด้วย nullif () แล้วโมฆะผลลัพธ์กับ isnull () คุณสามารถหลีกเลี่ยงการหารด้วยศูนย์ข้อผิดพลาด


3
เนื่องจากความยาวของคำตอบของคุณถูกแนะนำสำหรับการลบ โปรดทราบว่าการเพิ่มคำอธิบายเล็ก ๆ น้อย ๆ สำหรับสิ่งที่คุณแนะนำจะดีกว่าเสมอแม้ว่ามันจะดูเรียบง่ายมาก ๆ ก็ตาม)
Trinimon

10

การแทนที่ "หารด้วยศูนย์" ด้วยศูนย์คือการโต้เถียง - แต่มันก็ไม่ใช่ตัวเลือกเดียว ในบางกรณีการแทนที่ด้วย 1 คือ (สมเหตุสมผล) ที่เหมาะสม ฉันมักจะพบว่าตัวเองกำลังใช้

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

เมื่อฉันดูการเปลี่ยนแปลงของคะแนน / จำนวนและต้องการเริ่มต้นที่ 1 หากฉันไม่มีข้อมูล ตัวอย่างเช่น

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

บ่อยกว่าที่ฉันเคยคำนวณอัตราส่วนนี้ที่อื่น (ไม่น้อยเพราะมันสามารถโยนปัจจัยการปรับตัวที่ใหญ่มากสำหรับตัวส่วนต่ำในกรณีนี้ฉันมักจะควบคุม OldSampleScore มากกว่าเกณฑ์ซึ่งทำให้ไม่มีศูนย์ แต่บางครั้ง 'แฮ็ค' ก็เหมาะสม


1
@N Mason; ใช่บางครั้ง 1 เป็นตัวเลือก แต่คุณจะจำส่วน ISNULL ได้อย่างไรเมื่อคุณพิมพ์แบ็กสแลช
Henrik Staun Poulsen

1
ขออภัย Henrik - ฉันไม่แน่ใจว่าฉันเข้าใจคำถาม
N Mason

6

ฉันเขียนฟังก์ชัน a back back เพื่อจัดการกับโพรซีเดอร์ที่เก็บไว้ของฉัน:

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

2
สวัสดีรอนทางออกที่ดียกเว้นมีประเภทข้อมูลที่ จำกัด (ทศนิยม 4 ตำแหน่ง) และ @divisors ของเราอาจเป็นลบเช่นกัน และคุณบังคับใช้มันอย่างไร TIA Henrik Staun Poulsen
Henrik Staun Poulsen

1
ฉันรีบออกเร็ว ๆ นี้เพื่อจัดการกับสถานการณ์ปัญหาเฉพาะในเวลานั้น แอพสำหรับนักพัฒนาเดี่ยวดังนั้นการบังคับใช้จึงไม่ใช่เรื่องยากยกเว้นความทรงจำของฉัน :-)
Ron Savage

5
แม้จะมีคำสั่งพิมพ์ แต่มันไม่ใช่ proc ที่เก็บไว้มันเป็นสเกลาร์ UDF สิ่งนี้จะฆ่าคุณใน MS-SQL หากเป็นส่วนหนึ่งของแบบสอบถาม
Mark Sowul

4
ฉันเห็นด้วยกับการยืนยันของ Mark Sowul ว่าการทำงานของสเกลาร์จะทำให้เกิดอาการปวด นี่เป็นข้อเสนอแนะที่น่ากลัวใน T-SQL อย่าทำ! ฟังก์ชั่นสเกลาร์เป็นตัวทำลายประสิทธิภาพ! ฟังก์ชันในตารางที่มีค่าในบรรทัดเป็นเพียงฟังก์ชันผู้ใช้ที่ดีใน SQL Server (อาจมีข้อยกเว้นของฟังก์ชัน CLR ซึ่งสามารถทำงานได้ดี)
Davos

4
  1. เพิ่มข้อ จำกัด การตรวจสอบที่บังคับDivisorให้ไม่ใช่ศูนย์
  2. เพิ่มตัวตรวจสอบความถูกต้องให้กับฟอร์มเพื่อให้ผู้ใช้ไม่สามารถป้อนค่าศูนย์ลงในฟิลด์นี้

1
ฉันเริ่มชอบข้อ จำกัด การตรวจสอบมากขึ้นเรื่อย ๆ
Henrik Staun Poulsen

4

สำหรับการอัพเดต SQLs:

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

3
สวัสดี Vijay ใช่แล้วมันจะใช้งานได้ แต่ ... ฉันจะระวังส่วน ISNULL ซึ่งคุณต้องหารด้วย NULL ฉันอยากจะส่งสัญญาณไปยังผู้ใช้ว่าผลลัพธ์ไม่เป็นที่รู้จักเพราะตัวหารเป็นศูนย์
Henrik Staun Poulsen

2
มันช่วยฉันด้วยคำถามย่อยที่ซับซ้อนขอบคุณ
QMaster

3

ไม่มีการตั้งค่าส่วนกลางของเวทย์มนตร์ 'การแยกเลี้ยวด้วย 0 ข้อยกเว้นปิด' การดำเนินการจะต้องโยนเนื่องจากความหมายทางคณิตศาสตร์ของ x / 0 แตกต่างจากความหมาย NULL ดังนั้นจึงไม่สามารถส่งคืน NULL ได้ ฉันถือว่าคุณกำลังดูแลสิ่งที่ชัดเจนและข้อความค้นหาของคุณมีเงื่อนไขที่ควรกำจัดระเบียนด้วยตัวหาร 0 และไม่เคยประเมินการหาร 'gotcha' ตามปกติคือนักพัฒนาส่วนใหญ่คาดหวังว่า SQL จะทำตัวเหมือนภาษาเชิงโพรซีเดอร์และเสนอตัวดำเนินการเชิงตรรกะแบบลัดวงจร แต่ก็ไม่เป็นเช่นนั้น ฉันแนะนำให้คุณอ่านบทความนี้: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html


4
มี "การตั้งค่าระดับโลก Magic" เช่นนั้นตั้งค่า ARITHABORT OFF
David Manheim

3

นี่คือสถานการณ์ที่คุณสามารถหารด้วยศูนย์ได้ กฎทางธุรกิจคือการคำนวณการหมุนเวียนสินค้าคงคลังคุณใช้ต้นทุนสินค้าที่ขายเป็นระยะ หลังจากที่คุณมีหมายเลขรายปีคุณหารด้วยสินค้าคงคลังเฉลี่ยสำหรับรอบระยะเวลา

ฉันกำลังดูการคำนวณจำนวนสินค้าคงคลังที่เกิดขึ้นในช่วงสามเดือน ฉันคำนวณว่าฉันมีต้นทุนขายในช่วงสามเดือนที่มีราคา $ 1,000 อัตราการขายประจำปีคือ $ 4,000 ($ 1,000 / 3) * 12 สินค้าคงคลังเริ่มต้นคือ 0 สินค้าคงคลังสิ้นสุดคือ 0 สินค้าคงคลังเฉลี่ยของฉันตอนนี้คือ 0 ฉันมียอดขาย $ 4,000 ต่อปีและไม่มีสินค้าคงคลัง สิ่งนี้ให้ผลเป็นจำนวนอนันต์ หมายความว่าสินค้าคงคลังทั้งหมดของฉันกำลังถูกแปลงและซื้อโดยลูกค้า

นี่คือกฎทางธุรกิจของวิธีการคำนวณการหมุนเวียนสินค้าคงคลัง


3
ใช่แล้วคุณมีอนันต์จำนวนรอบ ดังนั้นในกรณีนี้ถ้าคุณมีการหารด้วยศูนย์คุณควรแสดงบางอย่างเช่น '#INF'
ตำรวจ SQL

1
"สินค้าคงคลังเริ่มต้นคือ 0 สินค้าคงคลังสิ้นสุดคือ 0 สินค้าคงคลังเฉลี่ยของฉันตอนนี้คือ 0" การคำนวณของคุณเป็นการประเมิน ในบางครั้งสินค้าคงคลังเป็นค่าบวกหรือคุณไม่สามารถจัดส่ง / ขายอะไรก็ได้ หากคุณไม่พอใจกับ + ∞ดังนั้นให้ใช้การประมาณค่าสินค้าคงคลังเฉลี่ยที่ดีขึ้น
Tom Blodget

2
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

ฉันไม่ชอบโซลูชันของคุณเนื่องจากการใช้ UDF บังคับให้เคียวรีรันในโหมดเธรดเดี่ยว ฉันชอบการตั้งค่าการทดสอบของคุณ ฉันอยากได้สิ่งนั้นใน UDF ทั้งหมดของเรา
Henrik Staun Poulsen

สำหรับฉันแล้วโซลูชันนี้สมบูรณ์แบบและสง่างาม
Payedimaunt

@Payedimaunt; ใช่ UDF นำไปสู่โค้ดที่สง่างามมาก แต่มันทำงานได้ไม่ดี นี่คือ "เคอร์เซอร์บนยานอนหลับ" :-) นอกจากนี้ยังเป็นเรื่องยากที่จะจำเขียน dbo.Divide แทน "/" ธรรมดา
Henrik Staun Poulsen


1

บางครั้ง 0 อาจไม่เหมาะสม แต่บางครั้ง 1 ก็ไม่เหมาะสม บางครั้งการกระโดดจาก 0 ถึง 100,000,000 อธิบายว่าการเปลี่ยนแปลง 1 หรือ 100 เปอร์เซ็นต์อาจทำให้เข้าใจผิด 100,000,000 เปอร์เซ็นต์อาจเหมาะสมในสถานการณ์นั้น ขึ้นอยู่กับประเภทของข้อสรุปที่คุณตั้งใจจะวาดตามเปอร์เซ็นต์หรืออัตราส่วน

ตัวอย่างเช่นสินค้าที่ขายน้อยมากย้ายจาก 2-4 ขายและสินค้าที่ขายขนาดใหญ่มากเปลี่ยนจาก 1,000,000 เป็น 2,000,000 ขายอาจหมายถึงสิ่งที่แตกต่างกันมากสำหรับนักวิเคราะห์หรือผู้บริหาร แต่ทั้งคู่จะผ่านมาเป็น 100% หรือ 1 เปลี่ยนแปลง

มันอาจจะง่ายกว่าที่จะแยกค่า NULL กว่าที่จะกัดเซาะมากกว่า 0% หรือ 100% แถวผสมกับข้อมูลที่ถูกต้อง บ่อยครั้งที่ 0 ในตัวส่วนสามารถระบุข้อผิดพลาดหรือค่าที่หายไปและคุณอาจไม่ต้องการเพียงเติมค่าโดยพลการเพียงเพื่อให้ชุดข้อมูลของคุณดูเป็นระเบียบ

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

1
ฉันพบว่าปัญหาคืออย่าลืมทำบางสิ่งเมื่อใดก็ตามที่คุณต้องการแบ่งงาน หากคุณจำไม่ได้ว่าการเพิ่ม CASE หรือ NULLIF คุณจะได้รับการช่วยเหลือใน x สัปดาห์ในเช้าวันจันทร์ ฉันเกลียดที่
Henrik Staun Poulsen

1

นี่คือวิธีที่ฉันแก้ไขมัน:

IIF (ValueA! = 0, ทั้งหมด / ValueA, 0)

สามารถห่อในการปรับปรุง:

SET Pct = IIF (ValueA! = 0, ทั้งหมด / ValueA, 0)

หรือเลือก:

SELECT IIF (ValueA! = 0, ทั้งหมด / ValueA, 0) เป็นเปอร์เซ็นต์จาก Tablename;

คิด?


ฉันและคนอื่น ๆ พบว่าการแทนที่ "ไม่ทราบ" ด้วยค่าศูนย์เป็นการแก้ไขที่เป็นอันตราย ฉันชอบ NULL มาก แต่สิ่งที่ยากคือต้องจำไว้ว่าให้เพิ่ม iif หรือ nullif ในทุกส่วน!
Henrik Staun Poulsen

0

คุณสามารถจัดการข้อผิดพลาดได้อย่างเหมาะสมเมื่อมันแพร่กระจายกลับไปที่โปรแกรมการโทร (หรือเพิกเฉยหากเป็นสิ่งที่คุณต้องการ) ใน C # ข้อผิดพลาดใด ๆ ที่เกิดขึ้นใน SQL จะโยนข้อยกเว้นที่ฉันสามารถจับแล้วจัดการในรหัสของฉันเช่นเดียวกับข้อผิดพลาดอื่น ๆ

ฉันเห็นด้วยกับ Beska ในที่ที่คุณไม่ต้องการซ่อนข้อผิดพลาด คุณอาจไม่ได้จัดการกับเครื่องปฏิกรณ์นิวเคลียร์ แต่การซ่อนข้อผิดพลาดโดยทั่วไปเป็นการฝึกเขียนโปรแกรมที่ไม่ดี นี่คือหนึ่งในเหตุผลที่ภาษาการเขียนโปรแกรมที่ทันสมัยส่วนใหญ่ใช้การจัดการข้อยกเว้นเชิงโครงสร้างเพื่อแยกค่าส่งคืนจริงด้วยรหัสข้อผิดพลาด / สถานะ โดยเฉพาะอย่างยิ่งเมื่อคุณกำลังทำคณิตศาสตร์ ปัญหาที่ใหญ่ที่สุดคือคุณไม่สามารถแยกความแตกต่างระหว่าง 0 ที่คำนวณได้อย่างถูกต้องถูกส่งกลับหรือ 0 เป็นผลมาจากข้อผิดพลาด แทนค่าใด ๆ ที่ส่งคืนเป็นค่าที่คำนวณได้และหากมีสิ่งใดผิดพลาดจะมีข้อผิดพลาดเกิดขึ้น แน่นอนว่าสิ่งนี้จะแตกต่างกันไปขึ้นอยู่กับวิธีที่คุณเข้าถึงฐานข้อมูลและภาษาที่คุณใช้ แต่คุณควรได้รับข้อความแสดงข้อผิดพลาดที่คุณสามารถจัดการได้เสมอ

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.
}

1
ฉันคิดว่าเราทุกคนยอมรับว่าการซ่อนข้อผิดพลาดด้วย 0 ไม่ใช่วิธีแก้ปัญหา สิ่งที่ฉันเสนอคือการเขียนโค้ดของเราเพื่อให้ทุก "/" ตามด้วย "NULLIF" วิธีนี้รายงานของฉัน / เครื่องปฏิกรณ์นิวเคลียร์ไม่ได้ถูกทิ้งไว้ตามลำพัง แต่กำลังแสดง "NULL" แทนที่จะเป็นข้อผิดพลาดที่น่ารำคาญข่าวสารเกี่ยวกับ 8134 สาเหตุถ้าฉันต้องการกระบวนการที่แตกต่างเมื่อตัวหารเป็น 0 แล้ว Beska และฉันเห็นด้วย เราจำเป็นต้องใช้รหัสนั้น มันยากที่จะทำทุกครั้งที่คุณเขียน "/" "/ NULLIF" เป็นไปไม่ได้ที่จะทำทุกครั้ง
Henrik Staun Poulsen

0

ใช้NULLIF(exp,0)แต่ด้วยวิธีนี้ -NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0)แบ่งถ้าประสบการณ์คือnullแต่NULLIF(ISNULL(exp,0),0)จะไม่แตก


1
ฉันไม่สามารถรับ NULLIF (exp, 0) ถ้า 0 เป็นศูนย์ไม่ใช่ o ลอง; ประกาศ @ i INT เลือก 1 / NULLIF (@i, 0) เพื่อดูว่าคุณสามารถทำให้มันพังได้หรือไม่
Henrik Staun Poulsen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.