HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

computed column as foreign key allow null

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
computedcolumnnullforeignallowkey

Problem

I have two tables.

TABLE A:

One of them stores some integer identificators, which are unique, but actually are structured entities. I will call them full_id.

Actually its a two parted indetificator, first byte of which is type of identificator. Lets call it type_id It can have values, which are divided in two groups:

-
group named '0x0f group': [0x01, 0x02, 0x03, 0x04].

-
group named '0xf0 group': [0x10, 0x20].

The last 3 bytes of full_id is arbitraty number and goes by name actual_id

So, it given that the full_ids are unique.
acutual_ids are also unique, but only in scope of 0x0f group of type_id values: there cant be equal actual_id coupled with any of those type_ids.

On the other hand - actual_ids are not unique in a scope of 0xf0 group of type_ids values and in practice there is plenty of duplicated values of actual_id coupled with those type_ids

in SQL table A goes like so:

CREATE TABLE A (
    full_id INTEGER NOT NULL PRIMARY KEY,
    actual_id  AS (
        CASE
            WHEN full_id & 0x0F000000 <> 0 THEN
                full_id & 0x00FFFFFF
            ELSE
                NULL
        END
    ) PERSISTED
)


TABLE B:

The second table contains only actual_ids which is supposed to have type_id within 0x0f group. This is caused by the data stream, from which the second table is populated and this is something I cant change.

in SQL table B goes like so:

CREATE TABLE B (
    actual_id INTEGER NOT NULL
)


I want to:

I want to apply a foreign key:

ALTER TABLE B WITH CHECK
ADD CONSTRAINT fk_a_actual_id FOREIGN KEY(actual_id) REFERENCES A(actual_id)


But I cant, because actual_id in table A must have unique constraint. And I cant apply unique constraint on calculated column. How can I overcome this situation?

My DBMS is Microsoft SQL Server 2012

Solution

You cannot create the contraint on A.actual_id because it may contains NULLs and there is no PK or unique index.
I didn't touch B because you said you cannot change it.


Here is what I did. It may work for you:

-
Create A_data where <> 0, Create A_null when = 0 and your Table B

Create Table dbo.A_data (
    full_id INTEGER NOT NULL PRIMARY KEY,
    actual_id  AS (CASE WHEN (full_id & 0x0F000000) <> 0 THEN full_id &  0x00FFFFFF ELSE NULL END) PERSISTED 
)
Create Table dbo.A_null (full_id INTEGER NOT NULL PRIMARY KEY)
Create Table dbo.B(actual_id int not null)


-
I can now create a unique index on A_data and a contraints on B which only contains not null value

Create UNIQUE INDEX idx_A_data On dbo.A_data(actual_id)
Go
ALTER Table B WITH CHECK ADD CONSTRAINT fk_a_actual_id FOREIGN KEY(actual_id) REFERENCES A_data(actual_id)
Go


-
I then create a view A which look like your table A

Create View A with schemabinding as 
    Select full_id, actual_id From dbo.A_data
    union all
    Select full_id, null From dbo.A_null


-
I finally create a instead of trigger on the view. It will give you the same insert possibilities than your old table A

Create Trigger triggerA on A Instead Of Insert
As
    Insert Into A_data(full_id) 
    select full_id from inserted where (full_id & 0x0F000000) <> 0

    Insert Into A_null(full_id) 
    select full_id from inserted where (full_id & 0x0F000000) = 0


-
You may also have to create Instead Of Update and Instead Of Delete triggers if required. It is better to keep them separated (1 trigger for each task: insert, delete, update)


Test

insert into A(full_id)values(0),(1),(2),(286331153)


0, 1 and 2 go to A_null because (full_id & 0x0F000000) = 0 and 286331153 goes to A_data

A contains:

  • 286331153 / 1118481



  • 0 / NULL



  • 1 / NULL



  • 2 / NULL

Code Snippets

Create Table dbo.A_data (
    full_id INTEGER NOT NULL PRIMARY KEY,
    actual_id  AS (CASE WHEN (full_id & 0x0F000000) <> 0 THEN full_id &  0x00FFFFFF ELSE NULL END) PERSISTED 
)
Create Table dbo.A_null (full_id INTEGER NOT NULL PRIMARY KEY)
Create Table dbo.B(actual_id int not null)
Create UNIQUE INDEX idx_A_data On dbo.A_data(actual_id)
Go
ALTER Table B WITH CHECK ADD CONSTRAINT fk_a_actual_id FOREIGN KEY(actual_id) REFERENCES A_data(actual_id)
Go
Create View A with schemabinding as 
    Select full_id, actual_id From dbo.A_data
    union all
    Select full_id, null From dbo.A_null
Create Trigger triggerA on A Instead Of Insert
As
    Insert Into A_data(full_id) 
    select full_id from inserted where (full_id & 0x0F000000) <> 0

    Insert Into A_null(full_id) 
    select full_id from inserted where (full_id & 0x0F000000) = 0
insert into A(full_id)values(0),(1),(2),(286331153)

Context

StackExchange Database Administrators Q#109184, answer score: 3

Revisions (0)

No revisions yet.