Eu quero ter uma restrição exclusiva em uma coluna que eu vou preencher com GUIDs. No entanto, meus dados contêm valores nulos para essas colunas. Como crio a restrição que permite vários valores nulos?
Aqui está um exemplo de cenário . Considere este esquema:
CREATE TABLE People (
Id INT CONSTRAINT PK_MyTable PRIMARY KEY IDENTITY,
Name NVARCHAR(250) NOT NULL,
LibraryCardId UNIQUEIDENTIFIER NULL,
CONSTRAINT UQ_People_LibraryCardId UNIQUE (LibraryCardId)
)
Então veja este código para o que estou tentando alcançar:
-- This works fine:
INSERT INTO People (Name, LibraryCardId)
VALUES ('John Doe', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');
-- This also works fine, obviously:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marie Doe', 'BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB');
-- This would *correctly* fail:
--INSERT INTO People (Name, LibraryCardId)
--VALUES ('John Doe the Second', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');
-- This works fine this one first time:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Richard Roe', NULL);
-- THE PROBLEM: This fails even though I'd like to be able to do this:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marcus Roe', NULL);
A declaração final falha com uma mensagem:
Violação da restrição UNIQUE KEY 'UQ_People_LibraryCardId'. Não é possível inserir chave duplicada no objeto 'dbo.People'.
Como posso alterar meu esquema e / ou restrição de exclusividade para permitir vários NULL
valores, enquanto continuo verificando a exclusividade nos dados reais?
sql-server
tsql
Stuart
fonte
fonte
null
não é um valor, mas a ausência de valor. De acordo com o padrão SQL,null
não é considerado igual anull
. Então, por que o múltiplonull
deve ser uma violação de exclusividade?Respostas:
SQL Server 2008 +
Você pode criar um índice exclusivo que aceite vários NULLs com uma
WHERE
cláusula. Veja a resposta abaixo .Antes do SQL Server 2008
Você não pode criar uma restrição UNIQUE e permitir NULLs. Você precisa definir um valor padrão de NEWID ().
Atualize os valores existentes para NEWID () onde NULL antes de criar a restrição UNIQUE.
fonte
O que você está procurando é de fato parte dos padrões ANSI SQL: 92, SQL: 1999 e SQL: 2003, ou seja, uma restrição UNIQUE deve proibir valores duplicados não NULL, mas aceitar vários valores NULL.
No mundo da Microsoft do SQL Server, no entanto, um único NULL é permitido, mas vários NULLs não são ...
No SQL Server 2008 , você pode definir um índice filtrado exclusivo com base em um predicado que exclui NULLs:
Nas versões anteriores, você pode recorrer ao VIEWS com um predicado NOT NULL para impor a restrição.
fonte
SQL Server 2008 e superior
Basta filtrar um índice exclusivo:
Nas versões inferiores, uma exibição materializada ainda não é necessária
Para o SQL Server 2005 e versões anteriores, você pode fazer isso sem uma exibição. Acabei de adicionar uma restrição única, como você está pedindo em uma das minhas tabelas. Dado que desejo exclusividade na coluna
SamAccountName
, mas desejo permitir vários NULLs, usei uma coluna materializada em vez de uma exibição materializada:Você simplesmente precisa colocar algo na coluna computada que será garantida exclusiva em toda a tabela quando a coluna exclusiva desejada real for NULL. Nesse caso,
PartyID
é uma coluna de identidade e ser numérico nunca corresponderá a nenhumSamAccountName
, portanto funcionou para mim. Você pode tentar seu próprio método - certifique-se de entender o domínio de seus dados para que não haja possibilidade de interseção com dados reais. Isso pode ser tão simples quanto adicionar um caractere diferenciador como este:Mesmo que
PartyID
um dia se torne não numérico e possa coincidir com umSamAccountName
, agora não importa.Observe que a presença de um índice incluindo a coluna computada faz com que cada resultado de expressão seja salvo no disco com os outros dados da tabela, o que ocupa espaço em disco adicional.
Observe que, se você não quiser um índice, ainda poderá salvar a CPU, fazendo com que a expressão seja pré-calculada em disco, adicionando a palavra
PERSISTED
- chave ao final da definição da expressão da coluna.No SQL Server 2008 e versões posteriores, use definitivamente a solução filtrada, se possível!
Controvérsia
Observe que alguns profissionais de banco de dados verão isso como um caso de "NULLs substitutos", que definitivamente apresentam problemas (principalmente devido a problemas ao tentar determinar quando algo é um valor real ou um valor substituto para a falta de dados ; também pode haver problemas com o número de valores substitutos que não sejam NULL multiplicando-se como loucos).
No entanto, acredito que este caso seja diferente. A coluna computada que estou adicionando nunca será usada para determinar nada. Ele não tem significado próprio e não codifica nenhuma informação que ainda não foi encontrada separadamente em outras colunas definidas corretamente. Nunca deve ser selecionado ou usado.
Então, minha história é que esse não é um NULL substituto, e estou cumprindo! Como na verdade, não queremos que o valor não-NULL seja para outra finalidade além de enganar o
UNIQUE
índice para ignorar NULLs, nosso caso de uso não apresenta nenhum dos problemas que surgem com a criação normal de NULL substituta.Tudo isso dito, não tenho nenhum problema em usar uma exibição indexada - mas isso traz alguns problemas, como a exigência de uso
SCHEMABINDING
. Divirta-se adicionando uma nova coluna à sua tabela base (você precisará, no mínimo, soltar o índice e, em seguida, soltar a visualização ou alterar a visualização para não ser vinculada ao esquema). Consulte a lista completa (longa) de requisitos para criar uma exibição indexada no SQL Server (2005) (também versões posteriores), (2000) .Atualizar
Se sua coluna for numérica, pode haver o desafio de garantir que a restrição exclusiva usando
Coalesce
não resulte em colisões. Nesse caso, existem algumas opções. Pode-se usar um número negativo, colocar os "NULL substitutos" somente na faixa negativa e os "valores reais" somente na faixa positiva. Como alternativa, o seguinte padrão pode ser usado. Na tabelaIssue
(ondeIssueID
está oPRIMARY KEY
), pode ou não haver umTicketID
, mas se houver, ele deve ser único.Se o IssueID 1 tiver o ticket 123, a
UNIQUE
restrição estará nos valores (123, NULL). Se o IssueID 2 não tiver um ticket, ele estará ativado (NULL, 2). Alguns pensam que mostrarão que essa restrição não pode ser duplicada para nenhuma linha da tabela e ainda permite vários NULLs.fonte
Para pessoas que estão usando o Microsoft SQL Server Manager e desejam criar um índice Único, mas que pode ser anulado, você pode criar seu índice exclusivo, como faria normalmente nas Propriedades do Índice para o seu novo índice, selecione "Filtro" no painel esquerdo e digite seu filtro (que é sua cláusula where). Deve ler algo como isto:
Isso funciona com o MSSQL 2012
fonte
Quando apliquei o índice exclusivo abaixo:
todas as atualizações e inserções não nulas falharam com o erro abaixo:
Encontrei isso no MSDN
Então, para que isso funcione corretamente, eu fiz isso
Eu acredito que é possível definir esta opção no código usando
mas eu não testei isso
fonte
Crie uma exibição que selecione apenas não
NULL
colunas e crie aUNIQUE INDEX
na exibição:Observe que você precisará executar
INSERT
'eUPDATE
' na exibição em vez de na tabela.Você pode fazer isso com um
INSTEAD OF
gatilho:fonte
Isso também pode ser feito no designer
Clique com o botão direito do mouse em Índice> Propriedades para obter esta janela
fonte
É possível criar uma restrição exclusiva em uma Visualização Indexada em Cluster
Você pode criar a visualização assim:
e a restrição única como esta:
fonte
Talvez considere um "
INSTEAD OF
" gatilho e faça a verificação você mesmo? Com um índice não clusterizado (não exclusivo) na coluna para ativar a pesquisa.fonte
Como afirmado anteriormente, o SQL Server não implementa o padrão ANSI quando se trata
UNIQUE CONSTRAINT
. Há um tíquete no Microsoft Connect para isso desde 2007. Conforme sugerido aqui e aqui, as melhores opções atualmente são usar um índice filtrado, conforme indicado em outra resposta ou coluna computada, por exemplo:fonte
Você pode criar um gatilho INSTEAD OF para verificar condições específicas e erros, se eles forem atendidos. Criar um índice pode ser caro em tabelas maiores.
Aqui está um exemplo:
fonte
Você não pode fazer isso com uma
UNIQUE
restrição, mas pode fazer isso em um gatilho.fonte
fonte
este código se você criar um formulário de registro com o textBox e usar insert e ur textBox estiver vazio e clicar no botão enviar.
fonte