patternMinor
computed column as foreign key allow null
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
Actually its a two parted indetificator, first byte of which is type of identificator. Lets call it
-
group named '
-
group named '
The last 3 bytes of
So, it given that the
On the other hand -
in SQL table A goes like so:
TABLE B:
The second table contains only
in SQL table B goes like so:
I want to:
I want to apply a foreign key:
But I cant, because
My DBMS is Microsoft SQL Server 2012
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_idSo, 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_idsin 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
-
I can now create a unique index on A_data and a contraints on B which only contains not null value
-
I then create a view A which look like your table A
-
I finally create a instead of trigger on the view. It will give you the same insert possibilities than your old table A
-
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
0, 1 and 2 go to A_null because
A contains:
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_dataA 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)
GoCreate View A with schemabinding as
Select full_id, actual_id From dbo.A_data
union all
Select full_id, null From dbo.A_nullCreate 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) = 0insert into A(full_id)values(0),(1),(2),(286331153)Context
StackExchange Database Administrators Q#109184, answer score: 3
Revisions (0)
No revisions yet.