ใช้ฟังก์ชั่น“ LEN” ในข้อ“ WHERE” ใน“ CREATE UNIQUE INDEX”


12

ฉันมีตารางนี้:

CREATE TABLE Table01 (column01 nvarchar(100));

และฉันต้องการสร้างดัชนีที่ไม่ซ้ำในคอลัมน์ 01ด้วยเงื่อนไขนี้LEN (คอลัมน์ 01)> = 5

ฉันเหนื่อย:

CREATE UNIQUE INDEX UIX_01 ON Table01(column01) WHERE LEN(column01) >= 5;

ฉันได้:

ส่วนคำสั่ง WHERE ของดัชนีที่กรองไม่ถูกต้อง 'UIX_01' บนโต๊ะ 'Table01'

และ:

ALTER TABLE Table01 ADD column01_length AS (LEN(column01));
CREATE UNIQUE INDEX UIX_01 ON Table01(column01) WHERE column01_length >= 5;

ผลิต:

ไม่สามารถสร้างดัชนีที่กรอง 'UIX_01' บนตาราง 'Table01' เนื่องจากคอลัมน์ 'column01_length' ในนิพจน์ตัวกรองเป็นคอลัมน์ที่คำนวณ เขียนซ้ำการแสดงออกของตัวกรองเพื่อที่จะไม่รวมคอลัมน์นี้

คำตอบ:


15

วิธีหนึ่งในการแก้ไขข้อ จำกัด ของดัชนีที่กรองคือมุมมองที่จัดทำดัชนี:

CREATE TABLE dbo.Table01 (
  Column01 NVARCHAR(100)
);
GO

CREATE VIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING AS
SELECT Column01
FROM dbo.Table01
WHERE LEN(Column01) >= 5;
GO

CREATE UNIQUE CLUSTERED INDEX cdx
    ON dbo.vw_Table01_Column01_LenOver5Unique(Column01);
GO

INSERT INTO dbo.Table01 VALUES('1'); --success
INSERT INTO dbo.Table01 VALUES('1'); --success
INSERT INTO dbo.Table01 VALUES('55555'); --success
INSERT INTO dbo.Table01 VALUES('55555'); --duplicate key error
GO

แก้ไข:

ฉันจะกำหนดมุมมองได้อย่างไรถ้าฉันมีสองคอลัมน์ในดัชนี สร้าง UNIQUE INDEX UIX_01 บน Table01 (คอลัมน์ 01, คอลัมน์ 02) WHERE LEN (คอลัมน์ 01)> = 5

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

CREATE TABLE dbo.Table01 (
   Column01 NVARCHAR(100)
  ,Column02 NVARCHAR(100)
);
GO

CREATE VIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING AS
SELECT Column01, Column02
FROM dbo.Table01
WHERE LEN(Column01) >= 5;
GO

CREATE UNIQUE CLUSTERED INDEX cdx
    ON dbo.vw_Table01_Column01_LenOver5Unique(Column01, Column02)
GO

INSERT INTO dbo.Table01 VALUES('1','A'); --success
INSERT INTO dbo.Table01 VALUES('1','A'); --success
INSERT INTO dbo.Table01 VALUES('55555','A'); --success
INSERT INTO dbo.Table01 VALUES('55555','B'); --success
INSERT INTO dbo.Table01 VALUES('55555','B'); --duplicate key error
GO

และฉันคาดหวังว่าสิ่งนี้จะทำงานได้ดีกว่าความชั่วร้ายของฉัน
James Anderson

@Dan Guzman ฉันควรใช้ 'กับ SCHEMABINDING' หรือไม่
geek

2
@Jalil ใช่SCHEMABINDINGจำเป็นสำหรับมุมมองที่จัดทำดัชนีไว้ ความหมายแน่นอนว่าคุณจะต้องวางมุมมองก่อนที่จะเปลี่ยนตาราง การใช้เครื่องมือเช่น SSDT จะดูแลการพึ่งพานั้นโดยอัตโนมัติ
Dan Guzman

ฉันจะกำหนดมุมมองได้อย่างไรถ้าฉันมีสองคอลัมน์ในดัชนี สร้าง UNIQUE INDEX UIX_01 บน Table01 (คอลัมน์ 01, คอลัมน์ 02) WHERE LEN (คอลัมน์ 01)> = 5;
geek

@Jalil ฉันเพิ่มตัวอย่างคีย์ผสมในคำตอบของฉัน
Dan Guzman

5

นี่เป็นข้อ จำกัด อีกประการหนึ่งของดัชนีที่กรองแล้ว การพยายามเลี่ยงผ่านด้วยการLIKEใช้WHERE column01 LIKE '_____'ไม่ได้ผลเช่นกันทำให้เกิดข้อความแสดงข้อผิดพลาดเดียวกัน ( "ข้อผิดพลาดWHERE clause ... " )

นอกจากVIEWวิธีแก้ปัญหาวิธีอื่นคือการแปลงคอลัมน์จากการคำนวณเป็นคอลัมน์ปกติและเพิ่มCHECKข้อ จำกัด เพื่อให้มีข้อมูลที่ถูกต้องเสมอ:

CREATE TABLE Table01 (column01 nvarchar(100),
                      column01_length int,
                      CHECK ( column01_length = len(column01)
                              AND column01 IS NOT NULL 
                              AND column01_length IS NOT NULL
                           OR column01 IS NULL 
                              AND column01_length IS NULL )
                     ) ;


CREATE UNIQUE INDEX UIX_01 ON Table01 (column01) WHERE column01_length >= 5 ;

ทดสอบที่rextester.com

โดยปกตินั่นหมายความว่าคุณต้องเติมcolumn01_lengthความยาวให้ถูกต้องทุกครั้งที่คุณเติมcolumn01(ในส่วนแทรกและอัพเดต) นั่นอาจเป็นเรื่องยุ่งยากเพราะคุณต้องตรวจสอบให้แน่ใจว่ามีการคำนวณความยาวเช่นเดียวกับLEN()ฟังก์ชันT-SQL โดยเฉพาะอย่างยิ่งจำเป็นต้องละเว้นช่องว่างต่อท้ายซึ่งไม่จำเป็นต้องคำนวณความยาวเป็นค่าเริ่มต้นในภาษาการเขียนโปรแกรมต่าง ๆ ที่เขียนแอปพลิเคชันไคลเอนต์ตรรกะอาจง่ายต่อการบัญชีสำหรับผู้โทร แต่คุณต้อง ตระหนักถึงความแตกต่างตั้งแต่แรก

ตัวเลือกจะเป็นINSERT/UPDATEทริกเกอร์1ในการจัดหาค่าที่ถูกต้องสำหรับคอลัมน์ดังนั้นจึงปรากฏขึ้นตามที่คำนวณได้กับแอปพลิเคชันไคลเอนต์


1ตามที่อธิบายไว้ในทริกเกอร์เปรียบเทียบกับข้อ จำกัดคุณจะต้องใช้ทริกเกอร์ INSTEAD OF แทนสำหรับสิ่งนี้ ทริกเกอร์ AFTER จะไม่มีวันดำเนินการเพราะความยาวที่ขาดหายไปจะทำให้ข้อ จำกัด การตรวจสอบล้มเหลวและในทางกลับกันจะป้องกันไม่ให้ทริกเกอร์ทำงาน อย่างไรก็ตามทริกเกอร์แทนมีข้อ จำกัด ของตนเอง (ดูแนวทางการวางแผนทริกเกอร์ DMLสำหรับภาพรวมอย่างรวดเร็ว)


1

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

CREATE TABLE dbo.Table01 
(
  Column01 NVARCHAR(100)
);
GO

CREATE FUNCTION dbo.ChkUniqueColumn01OverLen5()
RETURNS BIT
AS
BEGIN
DECLARE @Result BIT, @Count BIGINT, @DistinctCount BIGINT

SELECT  @Count = COUNT(Column01),
        @DistinctCount = COUNT(DISTINCT Column01)
FROM    Table01
WHERE   LEN(Column01) >= 5 

SELECT @Result = CASE WHEN @Count = @DistinctCount THEN 1 ELSE 0 END

RETURN @Result

END;
GO

ALTER TABLE dbo.Table01
ADD CONSTRAINT Chk_UniqueColumn01OverLen5
CHECK (dbo.ChkUniqueColumn01OverLen5() = 1);
GO

INSERT dbo.Table01 (Column01)
VALUES (N'123'), (N'1234');
GO

INSERT dbo.Table01 (Column01)
VALUES (N'12345');
GO

INSERT dbo.Table01 (Column01)
VALUES (N'12345'); -- Will fail
GO

INSERT dbo.Table01 (Column01)
VALUES (N'123'); -- Will pass
GO

UPDATE dbo.Table01
SET Column01 = '12345'
WHERE Column01 = '1234' -- Will fail
GO

SELECT * FROM dbo.Table01;
GO

DROP TABLE Table01;
DROP FUNCTION dbo.ChkUniqueColumn01OverLen5;

2
การใช้ฟังก์ชันที่มีค่าสเกลาร์ในข้อ จำกัด การตรวจสอบหรือคำจำกัดความคอลัมน์ที่คำนวณแล้วจะบังคับให้คิวรีทั้งหมดที่แตะที่ตารางนั้นทำงานตามลำดับแม้ว่าจะไม่ได้อ้างอิงคอลัมน์ก็ตาม
Erik Darling

2
@sp_BlitzErik Yep และนั่นอาจไม่ใช่สิ่งที่เลวร้ายที่สุดเกี่ยวกับวิธีแก้ปัญหานี้ :) ฉันแค่อยากจะดูว่ามันใช้งานได้หรือไม่
James Anderson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.