จะสร้างดัชนีเฉพาะในคอลัมน์ NULL ได้อย่างไร?


101

ฉันใช้ SQL Server 2005 ฉันต้องการ จำกัด ค่าในคอลัมน์ให้ไม่ซ้ำกันในขณะที่อนุญาต NULLS

โซลูชันปัจจุบันของฉันเกี่ยวข้องกับดัชนีเฉพาะในมุมมองดังนี้:

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

มีความคิดที่ดีกว่านี้ไหม


16
ไม่มีโอกาสใช้ sql 2008? คุณสามารถสร้างดัชนีที่กรองแล้วโดยใช้ 'where'
Simon_Weaver

3
คุณไม่ได้หมายความว่าไม่เหมือนใครโดยปล่อยให้ NULLsดูเหมือนว่าคุณมีความหมายไม่เหมือนใคร แต่รวมถึง NULL หลายตัวด้วย มิฉะนั้น NULL จะถูกสร้างดัชนีเหมือนกับค่าอื่น ๆ และข้อ จำกัด ของความเป็นเอกลักษณ์จะทำงานตามที่คาดไว้ - ไม่เป็นไปตามมาตรฐาน SQL ดังที่ @pst กล่าวไว้ในความคิดเห็นด้านล่าง
Suncat2000

คำตอบ:


26

ค่อนข้างแน่ใจว่าคุณไม่สามารถทำเช่นนั้นได้เนื่องจากละเมิดวัตถุประสงค์ของการไม่ซ้ำ

อย่างไรก็ตามดูเหมือนว่าบุคคลนี้จะทำงานได้ดี: http://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html


2
ดูเหมือนว่าเนื้อหาของลิงก์ที่คุณให้มานั้นถูกคัดลอกจริง (บางส่วน) โดยไม่ได้ระบุแหล่งที่มาจากที่นี่: decipherinfosys.wordpress.com/2007/11/30/…
Tom Juergens

77
ฉันไม่เห็นด้วยที่ "ละเมิดวัตถุประสงค์ของการไม่ซ้ำ" - NULL เป็นค่าพิเศษใน SQL (คล้ายกับ NaN ในหลาย ๆ ด้าน) และจำเป็นต้องปฏิบัติตามนั้น เป็นจริงความล้มเหลวในใน SQL Server ข้อกำหนดต่างๆ SQL เกียรติ: นี่คือการเชื่อมโยงสำหรับการร้องขอสำหรับ "การปฏิบัติที่ถูกต้อง" สำหรับสิ่งที่มันเป็นมูลค่าที่: connect.microsoft.com/SQLServer/feedback/details/299229/...

5
สำหรับการอ้างอิงในปี 2008 คุณสามารถสร้าง UNIQUE INDEX foo บน dbo.bar (คีย์) คีย์ WHERE ไม่เป็นโมฆะ
niico

2
ฉันไม่เห็นด้วยเช่นกันกับ "ละเมิดวัตถุประสงค์ของการไม่ซ้ำ" NULL ไม่เท่ากับ NULL ดังนั้นคุณควรสามารถสร้างดัชนีที่ไม่ซ้ำกันในคอลัมน์ที่เป็นโมฆะและแทรกค่าว่างหลายค่าได้
Wodzu

105

ใช้ SQL Server 2008 คุณสามารถสร้างดัชนีกรอง: http://msdn.microsoft.com/en-us/library/cc280372.aspx (ฉันเห็น Simon เพิ่มสิ่งนี้เป็นความคิดเห็น แต่คิดว่าสมควรได้รับคำตอบของตัวเองเนื่องจากความคิดเห็นนั้นพลาดได้ง่าย)

อีกทางเลือกหนึ่งคือทริกเกอร์ในการตรวจสอบความเป็นเอกลักษณ์ แต่อาจส่งผลต่อประสิทธิภาพ


84
create unique index UIX on MyTable (Column1) where Column1 is not null
Jørn Schou-Rode

1
หมายเหตุ: ขณะนี้ SQL Server Management Studio ดูเหมือนจะไม่ทราบวิธีสร้างดัชนีดังกล่าวดังนั้นหากคุณแก้ไขตารางในภายหลังมันจะสับสนและพยายามวางดังนั้นอย่าลืมสร้างขึ้นใหม่
Simon_Weaver

3
ดูเหมือนว่า Microsoft ได้อัปเดต SSMS เพื่อรองรับสิ่งนี้แล้ว ฉันมี SSMS 10.50.1617 และในกล่องโต้ตอบคุณสมบัติดัชนีคุณสามารถเลือกหน้าตัวกรองเพื่อแก้ไขตัวกรอง เช่น "([Column1] IS NOT NULL)"
Phil Haselden

5
การอนุญาตหลาย null ในดัชนีและการกรอง null จากดัชนีเป็นสิ่งที่แยกจากกัน การกรองดัชนีจะไม่รวมเร็กคอร์ดจากดัชนีในขณะที่โซลูชันอื่น ๆ จะเปลี่ยนค่า null เป็นค่าเฉพาะที่มีประโยชน์ ตระหนักถึงความแตกต่าง
Suncat2000

หากคุณใช้วิธีการเก็บไว้บนโต๊ะที่มีดัชนีกรองเช่นนั้นให้แน่ใจว่าANSI_NULLSเป็นONมิฉะนั้นคุณจะได้รับข้อผิดพลาดเมื่อพยายามที่จะแทรกข้อมูล
Arne

71

เคล็ดลับคอลัมน์จากการคำนวณเป็นที่รู้จักกันอย่างแพร่หลายว่า "nullbuster"; บันทึกของฉันเครดิต Steve Kass:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)

นี่เป็นเคล็ดลับเด็ด การค้นหา nullbuster อย่างผิดปกติจะไม่ทำให้เกิดสิ่งต่างๆมากเกินไป ฉันสงสัยว่าสิ่งนี้จะเป็นประโยชน์สำหรับการเร่งการค้นหาด้วยหรือไม่แทนที่จะเป็นคอลัมน์ที่คำนวณได้เพียง 1 และ 0 สำหรับค่า null หรือไม่หากการใช้ PK ทำให้ดัชนีมีอะไรให้ทำงานได้มากขึ้น? ไปทดสอบสุดสัปดาห์นี้บนโต๊ะใหญ่แล้วดู.
David Storfer

@DavidStorfer คุณไม่สามารถทำเช่นนั้นได้เนื่องจากคุณอาจมีการชนกันระหว่าง ID ของสองตารางที่แตกต่างกัน
user393274

ปรับปรุง: ISNULL (X, CONVERT (VARCHAR (10), pk))
Faiz

5
@ ฟาอิซ: การปรับปรุงอยู่ในสายตาของผู้มอง ฉันชอบรูปลักษณ์ของต้นฉบับมากกว่า
onedaywhen

@NunoG นี่ควรเป็นคำตอบที่ได้รับการยอมรับเนื่องจากเป็นโซลูชันที่ดีที่สอดคล้องกับความต้องการของคุณแทนที่จะเชื่อมโยงเว็บไซต์ภายนอกซึ่งอาจหายไป
Frédéric

-3

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

อย่างไรก็ตามนั่นไม่ได้หมายความว่าแนวคิดของ "คอลัมน์ที่ไม่ซ้ำกัน" นั้นถูกต้อง ในการนำไปใช้จริงในฐานข้อมูลเชิงสัมพันธ์ใด ๆ เราต้องจำไว้ว่าฐานข้อมูลประเภทนี้มีไว้เพื่อให้ทำงานได้อย่างถูกต้องและการทำให้เป็นมาตรฐานมักเกี่ยวข้องกับการเพิ่มตารางพิเศษ (ที่ไม่ใช่เอนทิตี) หลายตารางเพื่อสร้างความสัมพันธ์ระหว่างเอนทิตี .

มาดูตัวอย่างพื้นฐานโดยพิจารณาจาก "คอลัมน์ที่เป็นค่าว่างที่ไม่ซ้ำกัน" เพียงคอลัมน์เดียวจึงง่ายที่จะขยายไปยังคอลัมน์ดังกล่าวเพิ่มเติม

สมมติว่าเราแสดงข้อมูลด้วยตารางดังนี้:

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

เราสามารถทำได้โดยการแยก uniqnull ออกจากกันและเพิ่มตารางที่สองเพื่อสร้างความสัมพันธ์ระหว่างค่า uniqnull กับ the_entity (แทนที่จะมี uniqnull "inside" the_entity):

create table the_entity
(
  id integer,
  primary key(id)
);

create table the_relation
(
  the_entity_id integer not null,
  uniqnull integer not null,

  unique(the_entity_id),
  unique(uniqnull),
  /* primary key can be both or either of the_entity_id or uniqnull */
  primary key (the_entity_id, uniqnull), 
  foreign key (the_entity_id) references the_entity(id)
);

ในการเชื่อมโยงค่าของ uniqnull กับแถวใน _entity เราต้องเพิ่มแถวใน the_relation

สำหรับแถวใน _entity นั้นไม่มีการเชื่อมโยงค่า uniqnull (เช่นสำหรับแถวที่เราจะใส่ NULL ใน the_entity_incorrect) เราก็ไม่เพิ่มแถวใน the_relation

โปรดทราบว่าค่าสำหรับ uniqnull จะไม่ซ้ำกันสำหรับ the_relation ทั้งหมดและโปรดสังเกตด้วยว่าสำหรับแต่ละค่าใน _entity อาจมีได้ไม่เกินหนึ่งค่าใน the_relation เนื่องจากคีย์หลักและคีย์ภายนอกบังคับใช้สิ่งนี้

จากนั้นหากต้องเชื่อมโยงค่า 5 สำหรับ uniqnull กับ the_entity id ของ 3 เราจำเป็นต้อง:

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

และถ้าค่า id เป็น 10 สำหรับ the_entity ไม่มีคู่ uniqnull เราจะทำดังนี้

start transaction;
insert into the_entity (id) values (10); 
commit;

ในการทำให้ข้อมูลนี้ผิดปกติและรับข้อมูลที่ตารางเช่น the_entity_incorrect จะเก็บไว้เราจำเป็นต้อง:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

ตัวดำเนินการ "left outer join" ทำให้แน่ใจว่าแถวทั้งหมดจาก the_entity จะปรากฏในผลลัพธ์โดยใส่ NULL ในคอลัมน์ uniqnull เมื่อไม่มีคอลัมน์ที่ตรงกันอยู่ใน the_relation

โปรดจำไว้ว่าความพยายามใด ๆ ที่ใช้เวลาหลายวัน (หรือหลายสัปดาห์หรือหลายเดือน) ในการออกแบบฐานข้อมูลที่เป็นมาตรฐาน (และมุมมองและขั้นตอนการทำให้เป็นมาตรฐานที่สอดคล้องกัน) จะช่วยให้คุณประหยัดความเจ็บปวดและสิ้นเปลืองทรัพยากรได้เป็นเวลาหลายปี (หรือหลายทศวรรษ)


6
ตามที่ระบุไว้แล้วในความคิดเห็นของคำตอบที่ได้รับการยอมรับพร้อมด้วยคะแนนโหวตห้าสิบคะแนนควรได้รับการสนับสนุนโดยเซิร์ฟเวอร์ MS Sql เพื่อให้มีค่าว่างหลายค่าในคอลัมน์ที่จัดทำดัชนีว่าไม่ซ้ำกัน เป็นความล้มเหลวในการนำมาตรฐาน SQL ไปใช้โดยไม่อนุญาต Null ไม่ใช่ค่า null ไม่เท่ากับ null ซึ่งเป็นกฎพื้นฐานของ SQL ตั้งแต่ปี ดังนั้นประโยคแรกของคุณจึงไม่ถูกต้องและผู้อ่านส่วนใหญ่จะไม่รำคาญในการอ่าน
Frédéric
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.