SQL Server 2008 i nowsze wersje
Po prostu odfiltruj unikalny indeks:
CREATE UNIQUE NONCLUSTERED INDEX UQ_Party_SamAccountName
ON dbo.Party(SamAccountName)
WHERE SamAccountName IS NOT NULL;
W niższych wersjach widok zmaterializowany nadal nie jest wymagany
W przypadku SQL Server 2005 i wcześniejszych można to zrobić bez widoku. Właśnie dodałem unikalne ograniczenie, tak jakbyś prosił o jeden z moich stolików. Biorąc pod uwagę, że chcę wyjątkowość w kolumnie SamAccountName
, ale chcę pozwolić na wiele wartości NULL, użyłem zmaterializowanej kolumny zamiast zmaterializowanego widoku:
ALTER TABLE dbo.Party ADD SamAccountNameUnique
AS (Coalesce(SamAccountName, Convert(varchar(11), PartyID)))
ALTER TABLE dbo.Party ADD CONSTRAINT UQ_Party_SamAccountName
UNIQUE (SamAccountNameUnique)
Musisz po prostu umieścić w kolumnie obliczeniowej coś, co będzie gwarantowane jako unikalne w całej tabeli, gdy rzeczywista pożądana unikalna kolumna ma wartość NULL. W tym przypadku PartyID
jest kolumna tożsamości, a numeryczne nigdy nie pasuje do żadnej SamAccountName
, więc zadziałało dla mnie. Możesz wypróbować własną metodę - upewnij się, że rozumiesz domenę swoich danych, aby nie było możliwości krzyżowania się z rzeczywistymi danymi. Może to być tak proste, jak przygotowanie znaku wyróżniającego, takiego jak ten:
Coalesce('n' + SamAccountName, 'p' + Convert(varchar(11), PartyID))
Nawet jeśli PartyID
kiedyś stanie się nieliczbowy i może się pokrywać z SamAccountName
, teraz nie będzie to miało znaczenia.
Zauważ, że obecność indeksu zawierającego kolumnę obliczoną domyślnie powoduje zapisanie każdego wyniku wyrażenia na dysku z innymi danymi w tabeli, co NIE wymaga dodatkowego miejsca na dysku.
Zauważ, że jeśli nie chcesz indeksu, możesz nadal oszczędzać CPU, ustawiając wyrażenie na dysk, dodając słowo kluczowe PERSISTED
na końcu definicji wyrażenia kolumny.
W SQL Server 2008 i nowszych wersjach zdecydowanie użyj filtrowanego rozwiązania, jeśli to możliwe!
Spór
Należy pamiętać, że niektórzy specjaliści od baz danych uznają to za przypadek „zastępczych wartości NULL”, które zdecydowanie mają problemy (głównie z powodu problemów z ustaleniem, kiedy coś jest rzeczywistą wartością lub wartością zastępczą dla brakujących danych ; mogą też występować problemy z liczbą mnogich wartości zastępczych innych niż NULL mnożącymi się jak szalone).
Uważam jednak, że ta sprawa jest inna. Dodana kolumna obliczeniowa nigdy nie zostanie wykorzystana do ustalenia czegokolwiek. Nie ma ono żadnego znaczenia i nie koduje żadnych informacji, które nie zostały jeszcze znalezione osobno w innych, poprawnie zdefiniowanych kolumnach. Nigdy nie należy go wybierać ani używać.
Tak więc, moja historia jest taka, że to nie jest zastępcza wartość NULL i trzymam się tego! Ponieważ tak naprawdę nie chcemy wartości innej niż NULL w jakimkolwiek celu innym niż nakłonienie UNIQUE
indeksu do zignorowania wartości NULL, w naszym przypadku użycia nie ma żadnych problemów, które pojawiają się podczas normalnego tworzenia zastępczej wartości NULL.
To powiedziawszy, zamiast tego nie mam problemu z użyciem widoku indeksowanego, ale wiąże się to z pewnymi problemami, takimi jak wymóg użycia SCHEMABINDING
. Baw się dobrze, dodając nową kolumnę do tabeli podstawowej (musisz przynajmniej upuścić indeks, a następnie upuścić widok lub zmienić widok, aby nie był powiązany ze schematem). Zobacz pełną (długą) listę wymagań dotyczących tworzenia indeksowanego widoku w SQL Server (2005) (także późniejsze wersje), (2000) .
Aktualizacja
Jeśli twoja kolumna jest liczbowa, może istnieć wyzwanie, aby upewnić się, że użycie unikalnego ograniczenia Coalesce
nie spowoduje kolizji. W takim przypadku istnieje kilka opcji. Można użyć liczby ujemnej, aby „zastępować wartości NULL” tylko w zakresie ujemnym, a „wartości rzeczywiste” tylko w zakresie dodatnim. Alternatywnie można zastosować następujący wzór. W tabeli Issue
(gdzie IssueID
jest PRIMARY KEY
) może istnieć TicketID
, ale nie musi , ale jeśli istnieje, musi być unikalna.
ALTER TABLE dbo.Issue ADD TicketUnique
AS (CASE WHEN TicketID IS NULL THEN IssueID END);
ALTER TABLE dbo.Issue ADD CONSTRAINT UQ_Issue_Ticket_AllowNull
UNIQUE (TicketID, TicketUnique);
Jeśli IssueID 1 ma bilet 123, UNIQUE
ograniczenie będzie dotyczyło wartości (123, NULL). Jeśli IssueID 2 nie ma biletu, będzie włączony (NULL, 2). Pewna myśl pokaże, że tego ograniczenia nie można powielić dla żadnego wiersza w tabeli i nadal dopuszcza wiele wartości NULL.