เหตุใด ANSI SQL จึงกำหนด SUM (ไม่มีแถว) เป็น NULL


28

กำหนด ANSI SQL มาตรฐาน (บทที่ 6.5 เปฟังก์ชั่นชุด) พฤติกรรมต่อไปนี้สำหรับฟังก์ชันการรวมในชุดผลลัพธ์ที่ว่างเปล่า

COUNT(...) = 0
AVG(...) = NULL
MIN(...) = NULL
MAX(...) = NULL
SUM(...) = NULL

การส่งคืนค่า NULL สำหรับ AVG, MIN และ MAX เหมาะสมอย่างยิ่งเนื่องจากค่าเฉลี่ยค่าต่ำสุดและค่าสูงสุดของชุดว่างเปล่านั้นไม่ได้ถูกกำหนด

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

SUM({})        = 0    = 0
SUM({5})       = 5    = 0 + 5
SUM({5, 3})    = 8    = 0 + 5 + 3
SUM({5, NULL}) = NULL = 0 + 5 + NULL

การนิยามSUM({})ตามnullปกติทำให้ "ไม่มีแถว" เป็นกรณีพิเศษที่ไม่สอดคล้องกับสิ่งอื่น:

SUM({})     = NULL  = NULL
SUM({5})    = 5    != NULL + 5 (= NULL)
SUM({5, 3}) = 8    != NULL + 5 + 3 (= NULL)

มีข้อได้เปรียบที่ชัดเจนของตัวเลือกที่ทำ (SUM เป็น NULL) ที่ฉันพลาดหรือไม่


หมายเหตุ: นี่เป็นรุ่นทั่วไปของคำถามที่ผมได้ถามใน StackOverflow เฉพาะเกี่ยวกับ SQL เซิร์ฟเวอร์
Heinzi

5
ใช่ฉันเห็นด้วย: COUNT และ SUM ไม่ทำงานอย่างสม่ำเสมอ
AK

คำตอบ:


20

ฉันกลัวว่าเหตุผลก็คือกฎที่กำหนดไว้ในแบบเฉพาะกิจ (เช่น "คุณสมบัติ" อื่น ๆ ของISO SQL) ในเวลาที่การรวม SQL และการเชื่อมต่อของพวกเขากับคณิตศาสตร์น้อยกว่าตอนนี้ (*)

เป็นเพียงหนึ่งในความไม่สอดคล้องกันมากในภาษา SQL พวกเขาทำให้ภาษายากต่อการสอนยากที่จะเรียนรู้ยากที่จะเข้าใจมากขึ้นใช้งานได้ยากขึ้นกับสิ่งที่คุณต้องการ แต่นั่นเป็นเพียงสิ่งที่เป็นอยู่ กฎไม่สามารถเปลี่ยนแปลงได้ "เย็น" และ "เช่นเดียวกับ" ด้วยเหตุผลที่ชัดเจนของความเข้ากันได้แบบย้อนหลัง (ถ้าคณะกรรมการ ISO ตีพิมพ์มาตรฐานขั้นสุดท้ายแล้วผู้ขายก็กำหนดให้ใช้มาตรฐานนั้นผู้ขายเหล่านั้นจะไม่ชื่นชม มันมากถ้าในรุ่นที่ตามมากฎมีการเปลี่ยนแปลงการใช้งานที่มีอยู่ (ตาม) ที่มีอยู่ของรุ่นเก่าของมาตรฐาน "โดยอัตโนมัติล้มเหลวในการปฏิบัติตาม" รุ่นใหม่ ... )

(*) ในตอนนี้เป็นที่เข้าใจกันดีว่าการรวมตัวในชุดที่ว่างเปล่านั้นทำงานได้อย่างสม่ำเสมอมากขึ้นหากพวกเขาคืนค่าตัวตนของระบบอย่างเป็นระบบ(= สิ่งที่คุณเรียกว่า 'องค์ประกอบที่เป็นกลาง') ของผู้ประกอบการ ตัวดำเนินการไบนารีพื้นฐานนั้นสำหรับ COUNT และ SUM เป็นการเพิ่มเติมและค่าตัวตนของมันคือศูนย์ สำหรับ MIN และ MAX ค่าตัวตนนั้นเป็นค่าสูงสุดและต่ำสุดของประเภทที่อยู่ในมือตามลำดับหากประเภทที่เกี่ยวข้องมี จำกัด แม้ว่ากรณีเช่นค่าเฉลี่ยค่าเฉลี่ยฮาร์มอนิกมีเดีย ฯลฯ มีความซับซ้อนและแปลกใหม่ในแง่นี้


ฉันคิดว่าเป็นโมฆะทำให้รู้สึกถึงชุดที่ว่างกับนาทีและสูงสุด คุณอาจบอกว่าค่าตัวตนมีจริงไม่เป็นที่รู้จัก แต่ผลรวมของไม่มีค่าเป็น 0 ด้วยเหตุผลเดียวกับที่ n * 0 เสมอ 0 แต่นาทีและสูงสุดแตกต่างกัน ฉันไม่คิดว่าผลลัพธ์จะได้รับการกำหนดอย่างถูกต้องทำงานข้ามไม่มีบันทึก
Chris Travers

นอกจากนี้ avg () เหนือชุดว่างทำให้รู้สึกเป็นโมฆะเพราะ 0/0 ไม่ได้กำหนดไว้อย่างถูกต้องในบริบทนี้
Chris Travers

5
ต่ำสุดและสูงสุดไม่แตกต่างกัน รับตัวดำเนินการไบนารีพื้นฐาน LOWESTOF (x, y) และ HIGHESTOF (x, y) ตามลำดับ ตัวดำเนินการไบนารีเหล่านี้มีค่าเอกลักษณ์ เพราะในทั้งสองกรณี (ถ้าประเภทที่เกี่ยวข้องมี จำกัด ) มีค่าบางอย่างเช่น z ที่ forall x: LOWESTOF (z, x) = x และ forall y: HIGHESTOF (y, z) = y (ค่าตัวตนไม่เหมือนกันสำหรับทั้งสองกรณี แต่มันก็มีอยู่สำหรับทั้งสองกรณี) ฉันยอมรับว่าผลลัพธ์ดูขัดจังหวะอย่างมากในแวบแรก แต่ไม่มีการปฏิเสธความเป็นจริงทางคณิตศาสตร์
เออร์วิน Smout

@Erwin: ฉันเห็นด้วยกับทุกจุดของคุณยกเว้นว่าตัวตนของการดำเนินการบางอย่างHIGHEST()ไม่มากเป็นองค์ประกอบของประเภทข้อมูลเช่นสำหรับ Reals ที่ตัวตนจะเป็น-Infinity(และ+InfinityสำหรับLOWEST())
ypercubeᵀᴹ

1
@ กีวี SQL คุณลืมเกี่ยวกับการตรวจสอบประเภทคงที่? หากการแสดงออกของ SUM () ได้รับการจัดการโดยตัวตรวจสอบชนิดคงที่ราวกับว่าพวกเขาส่งกลับจำนวนเต็มเสมอเห็นได้ชัดว่ามันเป็นไปไม่ได้สำหรับการร้องขอ SUM () ที่บางครั้งส่งคืนสิ่งที่ไม่ใช่จำนวนเต็ม (เช่นความสัมพันธ์ว่างเปล่า)
เออร์วิน Smout

3

ในทางปฏิบัติผลลัพธ์ที่มีอยู่ในปัจจุบันNULLนั้นมีประโยชน์ พิจารณาตารางและข้อความต่อไปนี้:

C1 C2
-- --
 1  3 
 2 -1 
 3 -2 

SELECT SUM(C2) FROM T1 WHERE C1 > 9;

SELECT SUM(C2) FROM T1 WHERE C1 < 9;

คำสั่งแรกส่งคืน NULL และอันดับสองคืนค่าศูนย์ หากชุดว่างคืนค่าศูนย์ให้SUMเราต้องใช้วิธีอื่นในการแยกผลรวมจริงของศูนย์จากชุดว่างอาจใช้การนับ ถ้าเราต้องการให้ศูนย์สำหรับเซตว่างแล้วก็ง่าย ๆCOALESCEจะให้ความต้องการนั้น

SELECT COALESCE(SUM(C2),0) FROM T1 WHERE C1 > 9;

1
เป็นผลให้, SUM (รวมของ set1 และ set2) <> SUM (set1) + SUM (set2), เพราะหมายเลขใด ๆ + NULL = NULL มันสมเหตุสมผลกับคุณหรือไม่
AK

2
@Leigh: การใช้COALESCE()สิ่งนี้จะไม่แยกความแตกต่างของ0ผลรวม( ) ของชุดว่างจากNULLผลรวม () (พูดว่าตารางมี(10, NULL)แถว
ypercubeᵀᴹ

นอกจากนี้เรายังไม่สามารถแยกความแตกต่าง SUM (ชุดว่าง) จาก SUM (ชุดของ NULL อย่างน้อยหนึ่งรายการ) เราจำเป็นต้องแยกแยะเลยหรือไม่?
AK

@AlexKuznetsov - เราสามารถแยกผลรวมของชุดว่างจากผลรวมของชุดที่มีค่า Null หนึ่งค่าหรือมากกว่าตราบเท่าที่อย่างน้อยหนึ่งแถวมีค่า คุณถูกต้องว่าถ้าชุดมีเพียง NULL แล้วเราไม่สามารถแยกความแตกต่างของชุด NULL จากชุดของค่า NULL ทั้งหมดนี้ ประเด็นของฉันไม่ใช่ว่ามันมีประโยชน์ในทุกกรณีเพียงว่ามันมีประโยชน์ ถ้าฉันSUMเป็นคอลัมน์และกลับเป็นศูนย์ฉันรู้โดยไม่ต้องตรวจสอบว่ามีแถวอย่างน้อยหนึ่งแถวที่ไม่ใช่ค่า NULL ที่ใช้แสดงผลลัพธ์ให้ฉัน
Leigh Riffel

@ypercude - คุณถูกต้องอย่างแน่นอน ประเด็นของฉันคือพฤติกรรมในปัจจุบันของ SUM แยกแยะชุดว่างออกจากชุดที่มีค่า (แม้ว่าบางชุดจะเป็นโมฆะ) มันง่ายกว่าที่จะใช้ COALESCE เมื่อไม่จำเป็นต้องแยกความแตกต่างจากการใช้สิ่งต่าง ๆ เช่นDECODE(count(c2),0,NULL,sum(c2))เมื่อเป็น
Leigh Riffel

-1

ความแตกต่างหลักที่ฉันเห็นคือเกี่ยวกับประเภทข้อมูล COUNT มีประเภทผลตอบแทนที่กำหนดไว้อย่างดี: จำนวนเต็ม อื่น ๆ ทั้งหมดขึ้นอยู่กับประเภทของคอลัมน์ / การแสดงออกที่พวกเขากำลังดู ประเภทที่ส่งคืนของพวกเขาจะต้องเข้ากันได้กับสมาชิกทุกคนของชุด (คิดว่าลอยสกุลเงินทศนิยม, bcd, timespan, ... ) เนื่องจากไม่มีชุดคุณไม่สามารถบ่งบอกถึงชนิดส่งคืนได้ดังนั้น NULL จึงเป็นตัวเลือกที่ดีที่สุดของคุณ

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


5
ทำไมเราไม่สามารถบอกถึงประเภทการคืนในSUM(column)นิพจน์ได้ เราไม่มีตารางเปล่า - และมีคอลัมน์ทั้งหมดกำหนดประเภทไว้ใช่ไหม เหตุใดจึงแตกต่างกันสำหรับชุดผลลัพธ์ที่ว่างเปล่า
ypercubeᵀᴹ

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

คุณทั้งคู่อ่านบันทึกของฉันจริง ๆ หรือไม่? ใช่แล้วจะใช้งานได้กับ SUM ที่ยึดตามคอลัมน์ ณ ตอนนี้ แต่ทันทีที่คุณพบคอลัมน์ประเภทข้อมูลตัวแปร (ไม่ใช่ใน SQL Server - ยัง) คุณจะโชคไม่ดี
TToni

2
คุณจะกำหนดผลรวมอย่างไรในกรณีนั้น ผลลัพธ์ที่24 + 56.07 + '2012-10-05' + 'Red'ได้จะเป็นอย่างไร ฉันหมายความว่าไม่มีไพนต์กังวลว่าSUM()จะทำงานอย่างไรเมื่อเรามีปัญหาในการกำหนดเพิ่มเติม
ypercubeᵀᴹ

1
@TToni: "โดยเฉพาะอย่างยิ่งเมื่อคุณคิดถึงการขยายมาตรฐานที่เป็นไปได้" ไม่ใช่บริบทที่ OP อ้างถึง OP นั้นชัดเจนมากที่อ้างถึงเวอร์ชันปัจจุบันของมาตรฐานซึ่งไม่รวมถึงแนวคิดประเภท "ไดนามิกประเภท" หรืออะไรบางอย่าง (โอ้และฉันเพียงแค่แสดงความคิดเห็น แต่ไม่ downvote นอกเหนือจากใบเล็ก ๆ ที่ฉันมีปัญหากับไม่มีคำตอบของคุณเป็นสิ่งที่ผิดพอที่จะรับประกัน downvote IMO.)
Erwin Smout
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.