“BENZERSİZ DİZİNİ OLUŞTUR” daki “NEREDE” yan tümcesinde “LEN” işlevini kullanın


12

Bu tablo var:

CREATE TABLE Table01 (column01 nvarchar(100));

Ve bu koşul LEN (sütun01)> = 5 ile sütun01 üzerinde benzersiz bir dizin oluşturmak istiyorum

Denedim:

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

Bende var:

'Table01' tablosundaki 'UIX_01' filtrelenmiş dizini için yanlış WHERE yan tümcesi.

Ve :

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

üretir:

Filtre ifadesi 'UIX_01', 'Table01' tablosunda oluşturulamaz, çünkü filtre ifadesindeki 'column01_length' sütunu hesaplanan bir sütundur. Filtre ifadesini bu sütunu içermeyecek şekilde yeniden yazın.

Yanıtlar:


15

Filtrelenmiş dizin kısıtlamasına geçici bir çözüm bulmak için bir yöntem dizine alınmış bir görünümdür:

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

DÜZENLE:

Dizinde iki sütun varsa görünümü nasıl tanımlamalıyım? EŞSİZ İNDEKS UIX_01 AÇIK Tablo01 (sütun01, sütun02) NEREDE LEN (sütun01)> = 5

Dizin oluşturulmuş görünüm yaklaşımı, görünüm tanımına ve dizine başka anahtar sütunlar eklenerek bileşik bir anahtar için genişletilebilir. Görünüm tanımında aynı filtre uygulanır, ancak tek sütun değeri yerine bileşik anahtar tarafından uygulanan nitelikli satırların benzersizliği:

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

Ve bunun canavarlığımdan çok daha iyi performans göstermesini bekliyorum.
James Anderson

@ Guzman Guzman 'SCHEMABINDING İLE' kullanmalı mıyım?
inek

2
@Jalil Evet, SCHEMABINDINGdizine eklenmiş bir görünüm için gereklidir. Sonuçta tabiki tabloyu değiştirmeden önce görünümü düşürmeniz gerekecektir. SSDT gibi takımlar bu bağımlılığa otomatik olarak bakacaktır.
Dan Guzman

Dizinde iki sütun varsa görünümü nasıl tanımlamalıyım? EŞSİZ İNDEKS UIX_01 AÇIK Tablo01 (sütun01, sütun02) NEREDE LEN (sütun01)> = 5;
geek

@Jalil, cevabıma bileşik bir anahtar örnek ekledim.
Dan Guzman

5

Bu, filtrelenmiş dizinlerin birçok sınırlamasından biri gibi görünüyor. LIKEKullanarak atlamaya WHERE column01 LIKE '_____'çalışmak da aynı hata iletisini ( "Yanlış WHERE yan tümcesi ..." ) üreterek çalışmaz .

VIEWÇözümün yanı sıra, hesaplanan sütunu normal bir sütuna dönüştürmek ve CHECKher zaman geçerli verilere sahip olacak şekilde bir kısıtlama eklemek başka bir yol olacaktır :

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'da test edildi

Doğal olarak, bu, column01_lengthher doldurduğunuzda column01(eklemelerde ve güncellemelerde) açıkça doğru uzunlukta doldurmanız gerektiği anlamına gelir . Bu zor olabilir, çünkü uzunluğun T-SQL LEN()işlevinin yaptığı gibi hesaplandığından emin olmanız gerekir . Özellikle, sondaki boşlukların göz ardı edilmesi gerekir, bu da istemci uygulamalarının yazıldığı çeşitli programlama dillerinde uzunluk varsayılan olarak nasıl hesaplanacağı anlamına gelmez. ilk etapta farkın farkında.

Bir seçenek , sütun için doğru değeri sağlamak üzere bir INSERT/UPDATEtetikleyici 1 olacaktır, bu nedenle istemci uygulamalarına hesaplanmış olarak görünür.


1 Kısıtlayıcılarla karşılaştırıldığında Tetikleyiciler'de açıklandığı gibi , bunun için bir INSTEAD OF tetikleyicisi kullanmanız gerekir. AFTER tetikleyicisi hiçbir zaman yürütülmez, çünkü eksik uzunluk kontrol kısıtlamasında başarısız olur ve bu da tetikleyicinin çalışmasını önler. Bununla birlikte, INSTEAD OF tetikleyicilerin kendi kısıtlamaları vardır ( hızlı bir genel bakış için DML Tetikleyici Planlama Yönergeleri'ne bakın).


1

Bunun nasıl performans göstereceğinden emin değilim ve bunu göz ardı ettiğimden çok daha kolay bir yol olabilir, ancak bu sadece benzersizliği uygulamakla ilgileniyorsanız ihtiyacınız olanı yapmalıdır.

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
Denetim kısıtı veya hesaplanmış sütun tanımında skaler değerli bir işlev kullanılması, sütuna başvurmasalar bile tabloya dokunan tüm sorguların seri olarak çalışmasını zorlar.
Erik Darling

2
@sp_BlitzErik Evet ve bu çözümle ilgili en kötü şey bile olmayabilir :). Sadece işe yarayıp yaramayacağını görmek istedim, dolayısıyla performans uyarısı.
James Anderson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.