Search code examples
sql-serverforeign-keysunique-index

Can a UNIQUE 'WHERE' index which allows multiple NULLS be used as the primary key for a foreign key relationship?


Using SQL Server on Azure: 12.0.2000.8

I've defined the following unique index for TablePrimary:

CREATE UNIQUE NONCLUSTERED INDEX [IX_TablePrimary_Id] 
ON [dbo].[TablePrimary] ([PrimaryId] ASC)
WHERE [PrimaryId] IS NOT NULL

The WHERE clause allows multiple rows to have NULL in this column, but every non-NULL value in this column must be unique.

Should the indexed column in TablePrimary be suitable as the primary key for a foreign key relationship to TableForeign? Every non-NULL value must be unique and NULL in the foreign table wouldn't create a foreign key relationship anyway. But I am getting an error trying to place the foreign key relationship on TableForeign.

Unable to create relationship 'FK_TableForeign_TablePrimary'.
There are no primary or candidate keys in the referenced table 'dbo.TablePrimary' that match the referencing column list in the foreign key 'FK_TableForeign_TablePrimary'.

Before I dig deeper into this, I'd like to verify that what I am trying to do is indeed possible. Basically, not every row in TablePrimary will have children in TableForeign. But those rows which do exist in TableForeign must have a matching PrimaryId in TablePrimary (there are other ways to get the job done but in this case, I'd need a third table as the cross-ref between TablePrimary and TableForeign and I'd like to avoid that if it's not necessary, although, if it is necessary, then it is necessary).


Solution

  • Can a UNIQUE 'WHERE' index which allows multiple NULLS be used as the primary key for a foreign key relationship?

    No. Unique filtered indexes cannot be be referenced by foreign keys. Unique indexes (even with included columns, no filters though) can be referenced by foreign keys.

    edit: for Nullability. Foreign keys can reference unique indexes/constraints with NULLable columns. Nullability is not a prerequisite for creating foreign keys. However, foreign keys are NOT checked for rows with at least one null value in any of the fk columns:

    create table dbo.parent
    (
    id1 int null,
    id2 int null,
    constraint id1id2 unique(id1, id2)
    );
    
    insert into dbo.parent(id1, id2)
    values(1, 1), (2, 2), (3, null);
    
    go
    
    create table dbo.child
    (
    childid int identity,
    parentid1 int,
    parentid2 int,
    constraint fkparent foreign key(parentid1, parentid2) references parent(id1, id2)
    )
    go
    
    
    insert into dbo.child(parentid1, parentid2)
    values (1, 1), (2, 2) --ok
    go
    
    insert into dbo.child(parentid1, parentid2)
    values (4, 4) -- fk violation, there is no 4,4 parent row
    go
    
    insert into dbo.child(parentid1, parentid2)
    values (3, null) --do not get tricked here.... because the fk has a null value, the fk is NOT checked at all
    go
    
    --fk with one null value, fk is not checked at all 
    insert into dbo.child(parentid1, parentid2)
    values (100, null) -- but there is no 100, null parent row
    go
    
    select *
    from parent;
    select *
    from child;