SQL Constraint that one column value cannot be greater than another in a different table - sql

This probably isn't a very nice question as its blending business logic with DB structure, but its not my decision so:
Is it possible to define a constraint the infers the value of one column (Table A, Column X) cannot be greater than the value of another (Table B, Column Y) referenced via a foreign key:
TABLE_A
ID (Primary Key)
X (int value)
TABLE_B
A_ID (Foreign Key to TABLE_A)
Y (int value)
i.e. I want to enforce that for all values of Y, Y < L where L is a value from X where TABLE_B.A_ID == TABLE_A.ID
I'm working with DB2.

Is it possible to define a constraint the infers the value of one column (Table A, Column X) cannot be greater than the value of another (Table B, Column Y) referenced via a foreign key:
No. That would require using a SELECT statement within a CHECK constraint, and DB2 doesn't support that. If it did support using a SELECT statement that way, it would look something like this.
ALTER TABLE_B
ADD CONSTRAINT TABLE_B_Y_LT_L
CHECK (Y < (SELECT X FROM TABLE_A WHERE TABLE_A.ID = TABLE_B.A_ID));
The SELECT statement would return a single value, because TABLE_A.ID is unique. But, like I said, DB2 doesn't support SELECT statements in check constraints. I don't think any current dbms does.
Workarounds
There are a couple of workarounds. First, you could write a trigger. Second, you could store column "X" in both tables, and use a foreign key constraint and a check constraint to implement your requirement.
-- Since "id" is unique, the combination of "id" and "x" is also unique.
-- Declaring "unique (id, x)" lets these two columns be the target of a
-- foreign key reference.
--
create table table_a (
id integer primary key,
x integer not null,
unique (id, x)
);
-- The foreign key references both columns in "table_a". The check constraint
-- indirectly guarantees that y < table_a.x.
--
create table table_b (
a_id integer not null,
a_x integer not null,
y integer not null,
primary key (a_id, a_x, y),
foreign key (a_id, a_x) references table_a (id, x),
check (y < a_x)
);
This is standard SQL. It should work in any current SQL dbms.

Related

Can I make a unique constraint in Oracle on a combination of 2 columns from 2 different tables?

Column A is the PK of table A. Column B is a column in table B. Column C is a FK is table B that references column A in table A.
Can I define a constraint that says that that a column B AND column C have to be unique? As in, I don't want any repeats of the same combination of values from A and B.
Here's one possibility i'm thinking about:
Create a unique ID column.
unique_id varchar2 GENERATED ALWAYS AS (B || '-' || FK that references column A) VIRTUAL
set it as unique
CONSTRAINT unique_id UNIQUE
If i go with this solution i'm confused about one thing. The docs say: "Virtual columns are not supported for index-organized, external, object, cluster, or temporary tables." Obviously, because they don't get stored like other columns. Would this be a problem if I wanted to make my tables clustered?
Yes, you can create a UNIQUE constraint on two columns. For example:
create table table_a (
a number(6) primary key not null
);
create table table_b (
b number(6) not null,
c number(6) not null,
constraint uq1 unique (b, c),
constraint fk1 foreign key (c) references table_a (a)
);
Then, if you try to insert it will fail for duplicates. For example:
insert into table_a (a) values (1);
insert into table_a (a) values (2);
insert into table_b (b, c) values (10, 1);
insert into table_b (b, c) values (10, 1); -- fails!
insert into table_b (b, c) values (10, 2);
See running example at db<>fiddle.

How to create SQL constraint on primary key to make sure it could only be referenced once?

How do I add constraint to guard that a primary key could only be referenced once?(It could be referenced in two tables)
Each reference should have a unique value out of the primary key.
Table A
----------------------
id
1
2
3
4
Table B
----------------------
id a_id (foreign key to table A.id)
1 2
2 3
Table C
----------------------
id a_id (foreign key to table A.id)
1 1
I want something to happen to give error when try to insert a_id = 2 into table C as its used in table B already.
You can use an INSERT, UPDATE trigger on each of the child tables to ensure that the PK of the parent table that is about to be inserted or updated does not already exist in the other child table.
What you are trying to do requires another table D, that will help unify the references to A.
Table D will contain its own primary key ( Id ), a reference to table A with a UNIQUE constraint on it (call it AId ), and a third column (called "RowType") to indicate to which of the child tables (B or C) the row corresponds. You can make this column to be of type int, and assign value "0" for B and "1" for C, for example.
Then in table B you add a foreign key to D.Id, AND another column "BRowType" as foreign key to D.RowType; then you define a constraint on this column, so it can only have the value '0' ( or whatever value you have decided to correspond to this table).
For table C your constraint will limit the values to '1'.
Or course, in order to insert a record into B or C you first need to create a record in D. But once you have a record in B that references a record in D, which in turn links to a record in A, you will no longer be able to create a record in C for the same line in A - because of the UNIQUE constraint on D.AId AND the constraint on C.BRowType.
If I understand the question correctly, it sounds like you need to add a unique constraint on the column of each table that references your primary key.
For example:
Table A
----------------------
id (primary key)
1
2
3
Table B
----------------------
id a_id (foreign key to table A.id)
1 2
2 3
Set the a_id column to be UNIQUE and that way you can ensure that the primary key from Table A is not used twice. You would do that in each table which references A.id
If you want to avoid using triggers, you could create a table X with id and a unique constraint on it.
In each transaction in which you insert a record into B or C you have to insert into X as well. Both insertions will only be possible if not yet in the other table.

Unique column value combination with NULL values

I'm trying to conditionally insert some data into a table, where each combination of column values may only appear at most once in the table. The schema looks something like this:
CREATE TABLE foobar (
id SERIAL PRIMARY KEY,
a_id INTEGER,
b_id INTEGER,
c_id INTEGER,
ident VARCHAR(32),
date_a timestamp,
date_b timestamp,
FOREIGN KEY a_id REFERENCES a (id) ON DELETE CASCADE,
FOREIGN KEY b_id REFERENCES b (id) ON DELETE CASCADE,
FOREIGN KEY c_id REFERENCES c (id) ON DELETE CASCADE));
The combination of (a_id, b_id, c_id, ident) is unique, but only for rows where date_a AND date_b are both NULL.
I want to be able to insert a new row only if the a_id, b_id, c_id, task combination is not already in the db. if it is it doesn't have to do anything.
At first I tried to create a unique constraint on these columns, but the problem is that a_id, b_id and c_id are allowed to be NULL, as long as least one of them is not null. This ruins the unique constraint. Because the a, b and c_id fields are foreign keys, I can't set them to some other stub value (like -1).
I tried playing with locking (against my better judgement) which resulted in a deadlock within a couple of minutes of testing.
Is there any standard solution to this problem?
Rather than using a unique constraint, you should probably be using a unique index on the exact conditions you want to check for. You could then coalesce the null values into a dummy value, such as -1. Something like:
CREATE UNIQUE INDEX
foobar_test ON foobar
(COALESCE(a_id, -1), COALESCE(b_id, -1), COALESCE(c_id, -1), ident) -- Nulls become -1
WHERE date_a is null and date_b is null; -- Only check when date_a and date_b is null
This would make sure a_id, b_id, c_id, and ident were unique (including their combination of null values) for all rows where both date_a and date_b were null.
In a similar situations in multiple productive databases of mine I just add a "default row" to referenced tables. I use id = 0 for these and start real surrogate keys at 10 or 100. Or you could use -1 (or whatever suits you) if 0 is reserved.
This way I can set all fk columns to NOT NULL DEFAULT 0 and (partial) UNIQUE constraints work out of the box:
CREATE UNIQUE INDEX tbl_uni_idx ON tbl (a_id, b_id, c_id, ident)
WHERE date_a IS NULL AND date_b IS NULL;

Database design queries regarding inheritance and foreign key references

I have a query regarding a design problem that I faced …
There is a table A with subtypes B and C. Table A has an attribute type which tells whether the type is B or C. The common attributes of B and C are in A .
The problem is that there are no extra attributes for B .. all attributes required for B are in A already. However , there are extra attributes for C.
Is it an acceptable solution if I make tables A and C only ??… to extract entities of B I will query through the type attribute from table A
Can you refer any material ?
I also had a another confusion where table A has subtypes B,C,D . Table Z has a column that requires a value of primary id of either B or C but NOT D.
I thought of adding the primary id column of A as a foreign key reference to Z’s column and then making a trigger to ensure that the id isn't D ...
Can anyone please comment ?
Thank you !
Many people just enforce all these rules in application code. That is, they "simply" don't insert wrong data. Of course this is very fragile and depends on writing perfect application code at all times. So we want the database to enforce the constraints instead, so that wrong data can never be inserted.
CREATE TABLE A (
id INT PRIMARY KEY,
type CHAR(1) NOT NULL,
unique key (id, type)
);
CREATE TABLE B (
id INT PRIMARY KEY,
type CHAR(1) NOT NULL DEFAULT 'B',
FOREIGN KEY (id, type) REFERENCES A(id, type)
);
If you can force B.type to always be 'B' (CHECK constraint, trigger, or reference a one-row lookup table) then it can of course reference parent rows in A where type='B'. And do something similar in tables C and D, so then each row of A can be referenced by a row from only one sub-type table.
That is, if A.type is 'B' on a given row, and C.type can only be 'C', then no row of C can reference any row where A.type is 'B'.
Now if you want table Z to reference B or C but not D, you can reference by id and type, so Z also has its own type column. You can restrict Z.type by using a lookup table:
CREATE TABLE Ztypes (
type CHAR(1) PRIMARY KEY
);
INSERT INTO Ztypes VALUES ('B'), ('C');
CREATE TABLE Z (
id INT PRIMARY KEY,
Aid INT NOT NULL,
type CHAR(1) NOT NULL,
FOREIGN KEY (Aid, type) REFERENCES A(id, type),
FOREIGN KEY (type) REFERENCES Ztypes(type)
);
You've already got the answer you were looking for. But for other who run across this, it's worth researching two techniques: Class Table Inheritance and Shared Primary Key.
These two techniques used together make it fast, simple and easy to join A's data with either B's or C's data. And in this pattern, B contains only the key, but still contains usefull informaton.
Both of these techiques have their own tags.

A constraint that only allows one of two tables to reference a base table

I have 3 tables. A base table, call it Table A, and two tables that reference Table A, Call them Table X and Table Y. Both X and Y have a foreign key contraint that references Table A. The Foreign Key of X and Y is also their own Primary Key.
I'd like to know if it is possible to add a constraint that will only allow one of these tables to contain a recrod that references Table A. So if X has a record that references A then Y can't have one and if Y has a record that references A then X can't have one.
Is this possible?
Thanks,
CHECK constraints with UDFs (which is Oded's answer) don't scale well and have poor concurrency. See these:
Scalar UDFs wrapped in CHECK constraints are very slow and may fail for multirow updates
Tony Rogerson
So:
create a new table, say TableA2XY
this has the PK of TableA and a char(1) column with a CHECK to allow ony X or Y. And a unique constraint on the PK of A too.
tableX and tableY have new char(1) column with a check to allow only X or Y respectively
tableX and tableY have their FK to TableA2XY on both columns
This is the superkey or subtype approach
all DRI based
no triggers
no udfs with table access in CHECK constraints.
Yes, this is possible using CHECK constraints.
Apart from the normal foreign key constraint, you will need to add a CHECK constraint on both referencing tables to ensure that a foreign key is not used in the other referencing table.