เหตุใดข้อ จำกัด UNIQUE จึงอนุญาตให้ NULL เพียงหนึ่งอันเท่านั้น


36

ในทางเทคนิค NULL = NULL เป็นเท็จโดยตรรกะนั้น NULL จะเท่ากับ NULL ใด ๆ และ NULL ทั้งหมดนั้นแตกต่างกัน นี่ไม่ควรบอกเป็นนัยเลยว่า NULL ทั้งหมดนั้นไม่เหมือนใครและดัชนีที่ไม่ซ้ำกันควรอนุญาตให้มี NULL จำนวนเท่าใด?


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
พอลไวท์พูดว่า GoFundMonica

คำตอบ:


52

ทำไมมันถึงทำงานแบบนี้? เพราะย้อนกลับไปเมื่อมีคนตัดสินใจการออกแบบโดยไม่ทราบหรือสนใจสิ่งที่มาตรฐานพูด (หลังจากทั้งหมดเรามีพฤติกรรมแปลก ๆ ทุกชนิดด้วยNULLs และสามารถบีบบังคับพฤติกรรมที่แตกต่างได้ตามต้องการ) การตัดสินใจที่บอกว่าในนี้NULL = NULLกรณี

มันไม่ใช่การตัดสินใจที่ฉลาดมาก สิ่งที่พวกเขาควรจะทำคือมีการทำงานเริ่มต้นกับมาตรฐาน ANSI ยึดมั่นและถ้าพวกเขาอยากพฤติกรรมที่แปลกประหลาดนี้จริงๆให้มันผ่านตัวเลือก DDL เหมือนหรือWITH CONSIDER_NULLS_EQUALWITH ALLOW_ONLY_ONE_NULL

แน่นอนว่าปัญหาย้อนหลังคือ 20/20

และเรามีวิธีแก้ไขปัญหาตอนนี้แม้ว่ามันจะไม่สะอาดหรือใช้งานง่ายที่สุด

คุณสามารถรับพฤติกรรม ANSI ที่เหมาะสมใน SQL Server 2008 และสูงกว่าได้โดยสร้างดัชนีที่ไม่ซ้ำกันและถูกกรอง

CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;

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


8

แก้ไข. การใช้งานข้อ จำกัด หรือดัชนีที่ไม่ซ้ำกันในเซิร์ฟเวอร์ sql อนุญาตหนึ่ง NULL เดียว แก้ไขด้วยว่าเทคนิคนี้ไม่เหมาะกับคำจำกัดความของ NULL แต่เป็นหนึ่งในสิ่งที่พวกเขาทำเพื่อให้มีประโยชน์มากขึ้นแม้ว่าจะไม่ถูกต้อง "ทางเทคนิค" หมายเหตุคีย์หลัก (เช่นเดียวกับดัชนีที่ไม่ซ้ำกัน) ไม่อนุญาตให้ NULL (แน่นอน)


1
เทคนิคนี้ (ของ SQL-Server) ยังไม่สอดคล้องกับมาตรฐาน SQL มีรายการเชื่อมต่อ 7 ปีเกี่ยวกับปัญหานี้
ypercubeᵀᴹ

@ypercube True นั่นเป็นเหตุผลที่ฉันบอกว่ามันเป็นเพียงการดำเนินการและไม่สอดคล้องกับคำจำกัดความของ NULL จริงๆ ฉันไม่ได้คิดเกี่ยวกับดัชนีเฉพาะที่กรองแล้ว (แม้ว่าฉันจะใช้เพื่อสิ่งอื่น ๆ )
Kenneth Fisher

3

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

ทีนี้ลองนึกภาพต่อไปนี้ (โดยที่ฐานข้อมูลไม่มีความรู้ที่สมบูรณ์เกี่ยวกับสถานการณ์จำลอง)

Situation          Database

ID   Code          ID   Code
--   -----         --   -----
1    A             1    A
2    B             2    (null)
3    C             3    C
4    B             4    (null)

กฎความสมบูรณ์ที่เรากำลังสร้างแบบจำลองคือ "รหัสต้องไม่ซ้ำกัน" สถานการณ์ในโลกแห่งความจริงละเมิดสิ่งนี้ดังนั้นฐานข้อมูลไม่ควรอนุญาตให้ทั้งสองรายการและ 4 อยู่ในตารางในเวลาเดียวกัน

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

ผู้เขียนโปรแกรม Sybase ใช้วิธีที่ค่อนข้างปลอดภัยและไม่ยืดหยุ่นมากเพียงอนุญาตให้มีเครื่องหมายว่างหนึ่งอันในตารางซึ่งมีผู้แสดงความคิดเห็นบางคนบ่นตั้งแต่นั้นเป็นต้นมา Microsoft ยังคงมีพฤติกรรมนี้ต่อไปฉันเดาว่าความเข้ากันได้ย้อนหลัง


¹ฉันแน่ใจว่าฉันอ่านที่ไหนสักแห่งที่ Codd พิจารณาว่าใช้เครื่องหมาย null สองอัน - อันหนึ่งไม่เป็นที่รู้จักหนึ่งอันไม่เหมาะสม - แต่ปฏิเสธ แต่ฉันไม่สามารถหาข้อมูลอ้างอิงได้ ฉันจำได้ถูกต้องหรือไม่

PS คำพูดที่ฉันชอบเกี่ยวกับ null: Louis Davidson, "การออกแบบฐานข้อมูล SQL Server 2000 Professional", Wrox Press, 2001, หน้า 52 "ต้มให้เป็นประโยคเดียว: NULL is evil"


1
การปล่อยให้คนเดียวnullไม่บรรลุเป้าหมายนี้เช่นกัน เนื่องจากค่าที่หายไปอาจกลายเป็นค่าเดียวกับค่าหนึ่งในแถวอื่น
Martin Smith

1
สิ่งที่ @MartinSmith พูด เกิดอะไรขึ้นถ้าคุณมีข้อ จำกัด ในการตรวจสอบCHECK (Value IN ('A','B','C','D'))? จากนั้นทั้งการดำเนินการของ SQL-Server และมาตรฐาน SQL อนุญาตให้ตารางมี 5 แถว (หนึ่งแถวสำหรับแต่ละค่าบวก 1 ด้วย NULL) จากนั้นเนื้อหาในขณะที่ฐานข้อมูลสอดคล้องกับข้อ จำกัด ของมันไม่สอดคล้องกับเจตนาของนักออกแบบสำหรับ ตารางมีได้สูงสุด 4 แถว ไม่มีค่าใด ๆ ที่ NULL สามารถเปลี่ยนเป็นที่จะไม่ละเมิดข้อ จำกัด เว้นแต่จะลบแถวตั้งแต่หนึ่งแถวขึ้นไป
ypercubeᵀᴹ

1
ความจริงที่ว่ามาตรฐานจะอนุญาตให้ 6 ถึง 106 แถวแทนที่จะเป็น 5 จะไม่เปลี่ยนว่าทั้งคู่จะล้มเหลวในสถานการณ์นี้
ypercubeᵀᴹ

@ มาร์ตินสมิ ธ มันอาจ แต่หลังจากนั้นอีกครั้งมันอาจจะไม่ - เซิร์ฟเวอร์ฐานข้อมูลไม่สามารถบอกได้ว่าจะไม่เสี่ยงและใช้เส้นทางที่ปลอดภัย นั่นคือสิ่งที่โปรแกรมเมอร์ Sybase (ฉันเข้าใจ) ตัดสินใจสร้างความรำคาญตั้งแต่นั้นมา (อย่างน้อยก็ย้อนกลับไปที่ Inside SQL Server 6.5 หนังสือที่เก่าแก่ที่สุดในชั้นวางหนังสือของฉันซึ่ง Ron Soukup ให้ความคิดเห็นแบบเดียวกับที่ Aaron Bertrand ตอบ . ฉันเดาว่ามันอาจจะแย่กว่านี้ - พวกเขาไม่ได้บังคับเครื่องหมายไร้ค่า :-)
Greenstone Walker

2
@GreenstoneWalker - ไม่ใช้เส้นทาง "ปลอดภัย" สันนิษฐานว่าค่าที่หายไปจะไม่ขัดแย้ง CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;จะทำให้เกิดข้อผิดพลาด ตามทฤษฏีของคุณเกี่ยวกับแรงจูงใจในการออกแบบคุณควรป้องกันการแทรกNULLในกรณีแรก - เนื่องจากความรู้ที่ไม่สมบูรณ์หมายความว่าไม่มีการรับประกันว่ามูลค่าจะแตกต่างกัน
Martin Smith

2

นี่อาจไม่ถูกต้องทางเทคนิค แต่ในทางปรัชญามันช่วยให้ฉันนอนหลับตอนกลางคืน ...

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

ข้อ จำกัด ที่ไม่ซ้ำกันจะต้องมีค่าที่ชัดเจนสำหรับการเปรียบเทียบค่าของคอลัมน์ กล่าวอีกนัยหนึ่งเมื่อเปรียบเทียบค่าคอลัมน์เดียวกับค่าคอลัมน์อื่น ๆ โดยใช้ตัวดำเนินการความเท่าเทียมกันจะต้องประเมินค่าเป็นเท็จให้ถูกต้อง ไม่รู้จักไม่จริงเท็จแม้ว่ามันมักจะถือว่าเป็นเท็จ ค่า NULL สองค่าอาจเท่ากันหรือไม่ ... มันไม่สามารถกำหนดได้อย่างแน่นอน

ช่วยให้คิดถึงข้อ จำกัด ที่ไม่เหมือนใครในการ จำกัด ค่าที่สามารถกำหนดให้แตกต่างจากกัน สิ่งที่ฉันหมายถึงคือถ้าคุณเรียกใช้ SELECT ที่มีลักษณะดังนี้:

SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"

คนส่วนใหญ่คาดว่าจะมีผลลัพธ์เดียวเนื่องจากมีข้อ จำกัด ที่ไม่เหมือนใคร หากคุณอนุญาตให้มีค่า NULL หลายค่าใน ColumnWithUniqueConstraint คุณจะไม่สามารถเลือกแถวที่แตกต่างจากตารางโดยใช้ค่า NULL เป็นค่าที่เปรียบเทียบได้

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


Your Select จะให้ผล 1 ครั้งเมื่อมีข้อ จำกัด ที่ไม่ซ้ำกัน (ในการใช้งานใด ๆ ไม่ใช่เฉพาะ SQL-Server) ประเด็นของคุณคืออะไร?
ypercubeᵀᴹ

-3

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

มีบางกรณีที่หายากไม่กี่รายที่คอลัมน์ซึ่งมีUNIQUEข้อ จำกัด และมีค่า Null เพียงค่าเดียว ตัวอย่างเช่นถ้าตารางมีการทำแผนที่ระหว่างค่าของคอลัมน์และคำอธิบายข้อความที่แปลภาษา, แถวที่NULLจะทำให้มันเป็นไปได้ที่จะกำหนดรายละเอียดที่ควรจะปรากฏขึ้นเมื่อคอลัมน์ในตารางอื่น ๆ NULLบางอย่างคือ พฤติกรรมของการNULLอนุญาตสำหรับกรณีการใช้งานที่

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


3
ตัวระบุที่ไม่ซ้ำกันประดิษฐ์เป็นเรื่องตลกขอโทษ คุณจะทำอย่างไรกับ VIN ถ้าคุณไม่รู้ว่ามันคืออะไรทำไมต้องทำอะไรบางอย่าง เพียงเพื่อเพิ่มพื้นที่ว่างในดิสก์? ดูเหมือนว่าไร้สาระที่จะแก้ไขปัญหาอื่น ๆ (เช่นไม่ต้องการเขียนแอปพลิเคชันในลักษณะที่จัดการกับ NULL ได้อย่างงดงาม) หากคุณจำเป็นต้องรู้ว่าทำไมบางสิ่งบางอย่างเป็นโมฆะ (มีอยู่ แต่ไม่ทราบและรู้ว่ามันไม่มีอยู่เทียบกับไม่ทราบหรือไม่สนใจว่ามันมีอยู่แล้ว) เพิ่มคอลัมน์สถานะบางประเภท โทเค็นเพียงนำไปสู่การใช้รหัสที่ไม่พึงประสงค์เพื่อจัดการกับสิ่งเหล่านั้น
Aaron Bertrand

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

@AaronBertrand: มีบางกรณีที่เขตข้อมูลที่อาจเป็นโมฆะ - เฉพาะถ้าไม่เป็นโมฆะจะต้องเป็นกุญแจตัวแทนไม่สามารถสร้างขึ้นได้ล่วงหน้าในการเติมฟิลด์ (เช่น "รหัสคู่สมรส") แต่ในสถานการณ์เช่น ว่าข้อ จำกัด "ที่ไม่ซ้ำ" จะไม่เพียงพอ; มันจะจำเป็นว่าถ้า X.Spouse ไม่เป็นโมฆะ X.Spouse.Spouse = X บังเอิญบางอย่างเช่น "คู่สมรส" อาจได้รับการจัดการโดยบอกว่าบันทึกสำหรับผู้ที่ยังไม่ได้แต่งงานไม่ควรมี "NULL" เป็นคู่สมรส แต่เป็น ID ของตัวเองซึ่งในกรณีนี้กฎ X.spouse.spouse = X สามารถ นำไปใช้กับทุกคน
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.