Search code examples
sqlsql-server-2008foreign-keysconstraintscheck-constraints

Check Constraint Referencing Unique Column on another Table


I would like to limit the available values that can be entered in a column, but the values I would like to use are in another table, which is not the primary key of that table; the primary key of that table is not included in my table.

As an example, say I make a table for reporting of customers (Columns: Id, Name, Title) which references an existing table of customer titles (Columns: Id, Label) Both tables have primary keys on Id. I want the customers table to show the customer's title in words, not the title.Id associated with the customer. I know that the customer title column should only include values from the titles table. I cannot change the structure of the titles table.

Here are three options I have considered, but which I don't really like:

1.I cannot reference the title.label column with a foreign key as it is not a primary key.

2.I could create a check constraint and make a dynamic script to update it with the values in the titles table before updating the customers table with title labels, but this feels very hand cranked.

DECLARE @TableName VARCHAR(50) = 'Customers'
DECLARE @FieldName VARCHAR(50) = 'Title'
DECLARE @SQL VARCHAR(MAX) = ''

SELECT @SQL = @SQL + '
OR ' + @FieldName + ' = ''' + Label + ''''
FROM dbo.Titles
ORDER BY Label

SELECT @SQL = 'ALTER TABLE dbo.' + @TableName + ' DROP CONSTRAINT chk_Customer' + @FieldName + '
ALTER TABLE dbo.' + @TableName + ' ADD CONSTRAINT chk_Customer_' + @FieldName + ' CHECK
  (' + SUBSTRING(@SQL,6,LEN(@SQL)) + ')'

PRINT @SQL

3.I could just leave it be and forget about constraining the column at all, but I would rather add constraints where possible to keep everything battened down as much as possible and improve querying performance.

So, the question is this: is there a built-in way to reference one column (to limit or enumerate the values) by reference to another table, without having to script out the values in some way; something that once created will always be kept up to date, much as a foreign key relationship is but without the necessity to reference a primary key.


Solution

  • First, like GordonLinoff comments, the better approach is to include TitleID in the Customer table. Below is an option if you can't change the layout of the Customer table. A foreign key is definitely better than using dynamic T-SQL to keep a check constraint up to date.

    I cannot reference the title.label column with a foreign key as it is not a primary key.

    A foreign key can reference any candidate key. It doesn't have to reference the primary key.

    To tell the database about candidate keys, you can create a unique index:

    create table title (
        id int primary key, 
        label varchar(50));
    create table customer (
        id int primary key, 
        title varchar(50));
    create unique index ux_title_label on title(label);
    alter table customer add constraint fk_customer_title 
        foreign key (title) references title(label);
    

    Another way to tell the database about a candidate key is a unique constraint:

    alter table title add constraint uc_title_label unique (label);