Foreign Keys to Composite Key - sql

If I have a table where AId is the primary key and BId and CId are foreign keys referencing their tables. I need to make the combination of BId and CId unique.
How would I alter the table to make the combination unique?
Thanks
AId BId CId Notes Date
=== === === ===== ====
1 200 1 Random 2/2/2005
2 201 2 ETC 2/8/2007
3 202 3 ETC 2/12/2012

You need to create a unique index or a unique constraint.
I generally prefer unique indexes as they are more flexible (can add included columns if desired) and they don't have to be uniquely named in the schema.
Example syntax for a unique index
CREATE UNIQUE NONCLUSTERED INDEX SomeIndex ON YourTable(BId, CId)
The order of BId, CId makes no difference to the uniqueness guarantee but does affect the queries the index can efficiently support (that way round supports looking up by Bid or BId, CId but not CId)

Try this:
ALTER TABLE myTable
ADD CONSTRAINT myConstraint
UNIQUE NONCLUSTERED
(
BId, CId
)

You can create unique indexes that are not the primary key. This will force the combination of BId and CId unique.
CREATE UNIQUE INDEX ix_ATable_AltUniqueIndex ON ATable(BId,CId)
Or you can create a unique constraint
ALTER TABLE ATable ADD CONSTRAINT uni_ATable UNIQUE (BId,CId)
Create a unique constraint appears to create a unique index also.

Related

SQL Insert into table with 2 non-unique primary keys

I have a table that I need to insert data into.
The table Sales has 4 columns
CustomerValueType_id (int)
Customer_id (int)
Value (NVARCHAR)
Customer_name (NVARCHAR)
The CustomerValueType_id, Customer_id are foreign keys that are not-unique, CustomerValueType_id matches to a value type, while Customer_id matches to the Customer_name.
I need to add additional customer data into the Valuecolumn but how do I ensure that the data matches to the correct CustomerValueType_id and Customer_id and each customer name has to be repeated in the Customer_name
Sales Table
You are trying to do denormalization.
Ideally, in the normalized design, you will just store, parent_ids in
the child table. If you want to see parent_name fields, you have to
get them based on JOIN conditions.
But, here as you are storing both id, name in the child table, I would suggest you to create composite Foreign key in the child table, so that the combination is always present. Also, make this combination as composite primary key in the parent table.
-- Parent Table PRIMARY KEY
ALTER TABLE dbo.CustomerValueType
ADD CONSTRAINT PK_CustomerValueType
PRIMARY KEY CLUSTERED (CustomerValueType_id, Value);
GO
--Child Table FOREIGN KEY
ALTER TABLE dbo.Sales
ADD CONSTRAINT FK_Sales_CustomerValueType
FOREIGN KEY (CustomerValueType_id,Value)
REFERENCES dbo.CustomerValueType(CustomerValueType_id,Value);
GO

Why should we avoid CREATE UNIQUE INDEX? [duplicate]

As I can understand documentation the following definitions are equivalent:
create table foo (
id serial primary key,
code integer,
label text,
constraint foo_uq unique (code, label));
create table foo (
id serial primary key,
code integer,
label text);
create unique index foo_idx on foo using btree (code, label);
However, a note in the manual for Postgres 9.4 says:
The preferred way to add a unique constraint to a table is ALTER TABLE ... ADD CONSTRAINT. The use of indexes to enforce unique constraints
could be considered an implementation detail that should not be
accessed directly.
(Edit: this note was removed from the manual with Postgres 9.5.)
Is it only a matter of good style? What are practical consequences of choice one of these variants (e.g. in performance)?
I had some doubts about this basic but important issue, so I decided to learn by example.
Let's create test table master with two columns, con_id with unique constraint and ind_id indexed by unique index.
create table master (
con_id integer unique,
ind_id integer
);
create unique index master_unique_idx on master (ind_id);
Table "public.master"
Column | Type | Modifiers
--------+---------+-----------
con_id | integer |
ind_id | integer |
Indexes:
"master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
"master_unique_idx" UNIQUE, btree (ind_id)
In table description (\d in psql) you can tell unique constraint from unique index.
Uniqueness
Let's check uniqueness, just in case.
test=# insert into master values (0, 0);
INSERT 0 1
test=# insert into master values (0, 1);
ERROR: duplicate key value violates unique constraint "master_con_id_key"
DETAIL: Key (con_id)=(0) already exists.
test=# insert into master values (1, 0);
ERROR: duplicate key value violates unique constraint "master_unique_idx"
DETAIL: Key (ind_id)=(0) already exists.
test=#
It works as expected!
Foreign keys
Now we'll define detail table with two foreign keys referencing to our two columns in master.
create table detail (
con_id integer,
ind_id integer,
constraint detail_fk1 foreign key (con_id) references master(con_id),
constraint detail_fk2 foreign key (ind_id) references master(ind_id)
);
Table "public.detail"
Column | Type | Modifiers
--------+---------+-----------
con_id | integer |
ind_id | integer |
Foreign-key constraints:
"detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
"detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)
Well, no errors. Let's make sure it works.
test=# insert into detail values (0, 0);
INSERT 0 1
test=# insert into detail values (1, 0);
ERROR: insert or update on table "detail" violates foreign key constraint "detail_fk1"
DETAIL: Key (con_id)=(1) is not present in table "master".
test=# insert into detail values (0, 1);
ERROR: insert or update on table "detail" violates foreign key constraint "detail_fk2"
DETAIL: Key (ind_id)=(1) is not present in table "master".
test=#
Both columns can be referenced in foreign keys.
Constraint using index
You can add table constraint using existing unique index.
alter table master add constraint master_ind_id_key unique using index master_unique_idx;
Table "public.master"
Column | Type | Modifiers
--------+---------+-----------
con_id | integer |
ind_id | integer |
Indexes:
"master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
"master_ind_id_key" UNIQUE CONSTRAINT, btree (ind_id)
Referenced by:
TABLE "detail" CONSTRAINT "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
TABLE "detail" CONSTRAINT "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)
Now there is no difference between column constraints description.
Partial indexes
In table constraint declaration you cannot create partial indexes.
It comes directly from the definition of create table ....
In unique index declaration you can set WHERE clause to create partial index.
You can also create index on expression (not only on column) and define some other parameters (collation, sort order, NULLs placement).
You cannot add table constraint using partial index.
alter table master add column part_id integer;
create unique index master_partial_idx on master (part_id) where part_id is not null;
alter table master add constraint master_part_id_key unique using index master_partial_idx;
ERROR: "master_partial_idx" is a partial index
LINE 1: alter table master add constraint master_part_id_key unique ...
^
DETAIL: Cannot create a primary key or unique constraint using such an index.
One more advantage of using UNIQUE INDEX vs. UNIQUE CONSTRAINT is that you can easily DROP/CREATE an index CONCURRENTLY, whereas with a constraint you can't.
Uniqueness is a constraint. It happens to be implemented via the creation
of a unique index since an index is quickly able to search all existing
values in order to determine if a given value already exists.
Conceptually the index is an implementation detail and uniqueness should be
associated only with constraints.
The full text
So speed performance should be same
Since various people have provided advantages of unique indexes over unique constraints, here's a drawback: a unique constraint can be deferred (only checked at the end of the transaction), a unique index can not be.
A very minor thing that can be done with constraints only and not with indexes is using the ON CONFLICT ON CONSTRAINT clause (see also this question).
This doesn't work:
CREATE TABLE T (a INT PRIMARY KEY, b INT, c INT);
CREATE UNIQUE INDEX u ON t(b);
INSERT INTO T (a, b, c)
VALUES (1, 2, 3)
ON CONFLICT ON CONSTRAINT u
DO UPDATE SET c = 4
RETURNING *;
It produces:
[42704]: ERROR: constraint "u" for table "t" does not exist
Turn the index into a constraint:
DROP INDEX u;
ALTER TABLE t ADD CONSTRAINT u UNIQUE (b);
And the INSERT statement now works.
Another thing I've encountered is that you can use sql expressions in unique indexes but not in constraints.
So, this does not work:
CREATE TABLE users (
name text,
UNIQUE (lower(name))
);
but following works.
CREATE TABLE users (
name text
);
CREATE UNIQUE INDEX uq_name on users (lower(name));
There is a difference in locking.
Adding an index does not block read access to the table.
Adding a constraint does put a table lock (so all selects are blocked) since it is added via ALTER TABLE.
I read this in the doc:
ADD table_constraint [ NOT VALID ]
This form adds a new constraint to a table using the same syntax as CREATE TABLE, plus the option NOT VALID, which is currently only allowed for foreign key constraints. If the constraint is marked NOT VALID, the potentially-lengthy initial check to verify that all rows in the table satisfy the constraint is skipped. The constraint will still be enforced against subsequent inserts or updates (that is, they'll fail unless there is a matching row in the referenced table). But the database will not assume that the constraint holds for all rows in the table, until it is validated by using the VALIDATE CONSTRAINT option.
So I think it is what you call "partial uniqueness" by adding a constraint.
And, about how to ensure the uniqueness:
Adding a unique constraint will automatically create a unique B-tree index on the column or group of columns listed in the constraint. A uniqueness restriction covering only some rows cannot be written as a unique constraint, but it is possible to enforce such a restriction by creating a unique partial index.
Note: The preferred way to add a unique constraint to a table is ALTER TABLE … ADD CONSTRAINT. The use of indexes to enforce unique constraints could be considered an implementation detail that should not be accessed directly. One should, however, be aware that there’s no need to manually create indexes on unique columns; doing so would just duplicate the automatically-created index.
So we should add constraint, which creates an index, to ensure uniqueness.
How I see this problem?
A "constraint" aims to gramatically ensure that this column should be unique, it establishes a law, a rule; while "index" is semantical, about "how to implement, how to achieve the uniqueness, what does unique means when it comes to implementation". So, the way Postgresql implements it, is very logical: first, you declare that a column should be unique, then, Postgresql adds the implementation of adding an unique index for you.
SELECT a.phone_number,count(*) FROM public.users a
Group BY phone_number Having count(*)>1;
SELECT a.phone_number,count(*) FROM public.retailers a
Group BY phone_number Having count(*)>1;
select a.phone_number from users a inner join users b
on a.id <> b.id and a.phone_number = b.phone_number order by a.id;
select a.phone_number from retailers a inner join retailers b
on a.id <> b.id and a.phone_number = b.phone_number order by a.id
DELETE FROM
users a
USING users b
WHERE
a.id > b.id
AND a.phone_number = b.phone_number;
DELETE FROM
retailers a
USING retailers b
WHERE
a.id > b.id
AND a.phone_number = b.phone_number;
CREATE UNIQUE INDEX CONCURRENTLY users_phone_number
ON users (phone_number);
To Verify:
insert into users(name,phone_number,created_at,updated_at) select name,phone_number,created_at,updated_at from users

Unique constraint vs. unique index?

Is there a difference between:
CREATE TABLE p(
product_no integer,
name text UNIQUE,
price numeric
);
and:
CREATE TABLE p(
product_no integer,
name text,
price numeric
);
CREATE UNIQUE INDEX customername
ON p
USING btree
(name COLLATE pg_catalog."default");
Will name be unique in both cases? What does it means when an index is unique?
EDIT: Postgres unique constraint vs index isn't answering my question. It considers a case with FK. My question has nothing to do with FK's. I just want to know if these two operations are equivalent in this example where no FK is involved.
Yes, there's a small difference. If you define a unique constraint it's visible in catalogs like information_schema. This is not true of a unique index.
Also, you can create things like partial unique indexes, but you cannot do that on a constraint.
Finally, unique constraints are SQL-standard.
A unique constraint implies the creation of a unique index, but not vice versa.
Use a unique constraint unless you have a good reason to create the unique index directly.
From documentation
Adding a unique constraint will automatically create a unique btree
index on the column or group of columns used in the constraint
So for your simplified example they are equivalent

Postgres unique constraint vs index

As I can understand documentation the following definitions are equivalent:
create table foo (
id serial primary key,
code integer,
label text,
constraint foo_uq unique (code, label));
create table foo (
id serial primary key,
code integer,
label text);
create unique index foo_idx on foo using btree (code, label);
However, a note in the manual for Postgres 9.4 says:
The preferred way to add a unique constraint to a table is ALTER TABLE ... ADD CONSTRAINT. The use of indexes to enforce unique constraints
could be considered an implementation detail that should not be
accessed directly.
(Edit: this note was removed from the manual with Postgres 9.5.)
Is it only a matter of good style? What are practical consequences of choice one of these variants (e.g. in performance)?
I had some doubts about this basic but important issue, so I decided to learn by example.
Let's create test table master with two columns, con_id with unique constraint and ind_id indexed by unique index.
create table master (
con_id integer unique,
ind_id integer
);
create unique index master_unique_idx on master (ind_id);
Table "public.master"
Column | Type | Modifiers
--------+---------+-----------
con_id | integer |
ind_id | integer |
Indexes:
"master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
"master_unique_idx" UNIQUE, btree (ind_id)
In table description (\d in psql) you can tell unique constraint from unique index.
Uniqueness
Let's check uniqueness, just in case.
test=# insert into master values (0, 0);
INSERT 0 1
test=# insert into master values (0, 1);
ERROR: duplicate key value violates unique constraint "master_con_id_key"
DETAIL: Key (con_id)=(0) already exists.
test=# insert into master values (1, 0);
ERROR: duplicate key value violates unique constraint "master_unique_idx"
DETAIL: Key (ind_id)=(0) already exists.
test=#
It works as expected!
Foreign keys
Now we'll define detail table with two foreign keys referencing to our two columns in master.
create table detail (
con_id integer,
ind_id integer,
constraint detail_fk1 foreign key (con_id) references master(con_id),
constraint detail_fk2 foreign key (ind_id) references master(ind_id)
);
Table "public.detail"
Column | Type | Modifiers
--------+---------+-----------
con_id | integer |
ind_id | integer |
Foreign-key constraints:
"detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
"detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)
Well, no errors. Let's make sure it works.
test=# insert into detail values (0, 0);
INSERT 0 1
test=# insert into detail values (1, 0);
ERROR: insert or update on table "detail" violates foreign key constraint "detail_fk1"
DETAIL: Key (con_id)=(1) is not present in table "master".
test=# insert into detail values (0, 1);
ERROR: insert or update on table "detail" violates foreign key constraint "detail_fk2"
DETAIL: Key (ind_id)=(1) is not present in table "master".
test=#
Both columns can be referenced in foreign keys.
Constraint using index
You can add table constraint using existing unique index.
alter table master add constraint master_ind_id_key unique using index master_unique_idx;
Table "public.master"
Column | Type | Modifiers
--------+---------+-----------
con_id | integer |
ind_id | integer |
Indexes:
"master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
"master_ind_id_key" UNIQUE CONSTRAINT, btree (ind_id)
Referenced by:
TABLE "detail" CONSTRAINT "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
TABLE "detail" CONSTRAINT "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)
Now there is no difference between column constraints description.
Partial indexes
In table constraint declaration you cannot create partial indexes.
It comes directly from the definition of create table ....
In unique index declaration you can set WHERE clause to create partial index.
You can also create index on expression (not only on column) and define some other parameters (collation, sort order, NULLs placement).
You cannot add table constraint using partial index.
alter table master add column part_id integer;
create unique index master_partial_idx on master (part_id) where part_id is not null;
alter table master add constraint master_part_id_key unique using index master_partial_idx;
ERROR: "master_partial_idx" is a partial index
LINE 1: alter table master add constraint master_part_id_key unique ...
^
DETAIL: Cannot create a primary key or unique constraint using such an index.
One more advantage of using UNIQUE INDEX vs. UNIQUE CONSTRAINT is that you can easily DROP/CREATE an index CONCURRENTLY, whereas with a constraint you can't.
Uniqueness is a constraint. It happens to be implemented via the creation
of a unique index since an index is quickly able to search all existing
values in order to determine if a given value already exists.
Conceptually the index is an implementation detail and uniqueness should be
associated only with constraints.
The full text
So speed performance should be same
Since various people have provided advantages of unique indexes over unique constraints, here's a drawback: a unique constraint can be deferred (only checked at the end of the transaction), a unique index can not be.
A very minor thing that can be done with constraints only and not with indexes is using the ON CONFLICT ON CONSTRAINT clause (see also this question).
This doesn't work:
CREATE TABLE T (a INT PRIMARY KEY, b INT, c INT);
CREATE UNIQUE INDEX u ON t(b);
INSERT INTO T (a, b, c)
VALUES (1, 2, 3)
ON CONFLICT ON CONSTRAINT u
DO UPDATE SET c = 4
RETURNING *;
It produces:
[42704]: ERROR: constraint "u" for table "t" does not exist
Turn the index into a constraint:
DROP INDEX u;
ALTER TABLE t ADD CONSTRAINT u UNIQUE (b);
And the INSERT statement now works.
Another thing I've encountered is that you can use sql expressions in unique indexes but not in constraints.
So, this does not work:
CREATE TABLE users (
name text,
UNIQUE (lower(name))
);
but following works.
CREATE TABLE users (
name text
);
CREATE UNIQUE INDEX uq_name on users (lower(name));
There is a difference in locking.
Adding an index does not block read access to the table.
Adding a constraint does put a table lock (so all selects are blocked) since it is added via ALTER TABLE.
I read this in the doc:
ADD table_constraint [ NOT VALID ]
This form adds a new constraint to a table using the same syntax as CREATE TABLE, plus the option NOT VALID, which is currently only allowed for foreign key constraints. If the constraint is marked NOT VALID, the potentially-lengthy initial check to verify that all rows in the table satisfy the constraint is skipped. The constraint will still be enforced against subsequent inserts or updates (that is, they'll fail unless there is a matching row in the referenced table). But the database will not assume that the constraint holds for all rows in the table, until it is validated by using the VALIDATE CONSTRAINT option.
So I think it is what you call "partial uniqueness" by adding a constraint.
And, about how to ensure the uniqueness:
Adding a unique constraint will automatically create a unique B-tree index on the column or group of columns listed in the constraint. A uniqueness restriction covering only some rows cannot be written as a unique constraint, but it is possible to enforce such a restriction by creating a unique partial index.
Note: The preferred way to add a unique constraint to a table is ALTER TABLE … ADD CONSTRAINT. The use of indexes to enforce unique constraints could be considered an implementation detail that should not be accessed directly. One should, however, be aware that there’s no need to manually create indexes on unique columns; doing so would just duplicate the automatically-created index.
So we should add constraint, which creates an index, to ensure uniqueness.
How I see this problem?
A "constraint" aims to gramatically ensure that this column should be unique, it establishes a law, a rule; while "index" is semantical, about "how to implement, how to achieve the uniqueness, what does unique means when it comes to implementation". So, the way Postgresql implements it, is very logical: first, you declare that a column should be unique, then, Postgresql adds the implementation of adding an unique index for you.
SELECT a.phone_number,count(*) FROM public.users a
Group BY phone_number Having count(*)>1;
SELECT a.phone_number,count(*) FROM public.retailers a
Group BY phone_number Having count(*)>1;
select a.phone_number from users a inner join users b
on a.id <> b.id and a.phone_number = b.phone_number order by a.id;
select a.phone_number from retailers a inner join retailers b
on a.id <> b.id and a.phone_number = b.phone_number order by a.id
DELETE FROM
users a
USING users b
WHERE
a.id > b.id
AND a.phone_number = b.phone_number;
DELETE FROM
retailers a
USING retailers b
WHERE
a.id > b.id
AND a.phone_number = b.phone_number;
CREATE UNIQUE INDEX CONCURRENTLY users_phone_number
ON users (phone_number);
To Verify:
insert into users(name,phone_number,created_at,updated_at) select name,phone_number,created_at,updated_at from users

Constraints and optimal table design

Here's my design as is
I want a constraint that will ensure only (at most) one result of
select ID
from A a, B b
where a.ID = b.PartialKey_Ref_A
and a.PartCandidateB = 'valueA'
and b.PartialKeyB = 'valueB'
Incidentally (perhaps changes the optimal design) I want at most one result from
select ID
from A
where PartCandidateA = 'valueA2'
and PartCandidateB = 'valueB2'
How can I enforce the constraint and optimize the design?
I assume that where you write Key, you mean Unique or Primary Key. And that ID means a surrogate (auto-generated) identifier. With these assumptions, the two tables are in 1:n relationship and you could change them into:
Table A
-------
PartCandidateA
PartCandidateB
ID
PRIMARY KEY (ID)
UNIQUE KEY (PartCandidateA, PartCandidateB) --- or PRIMARY if you drop the ID
--- this is your second constraint
Table B
-------
PartCandidateA
PartCandidateB
PartialKeyB
PRIMARY KEY (PartCandidateB, PartialKeyB) --- or UNIQUE
--- this is your first constraint
FOREIGN KEY (PartCandidateA, PartCandidateB)
REFERENCES A (PartCandidateA, PartCandidateB)
So, your query to find the ID will be written as:
SELECT ID
FROM A a, B b
WHERE a.PartCandidateA = b.PartCandidateA
AND a.PartCandidateB = b.PartCandidateB
AND b.PartCandidateB = 'valueA'
AND b.PartialKeyB = 'valueB'
I think I need to add the PartCandidateB column to Table B.
Then I can add the unique constraint on (PartialKeyB,PartCandidateB).
This will increase the DB by sizeof(PartCandidateB)*rows in TableB.
But the constraint will be enforced:)
I don't think this introduces any problems other than the size increase thing
Thanks to everyone
You can simply create a unique contraint on those two columns by creating a unique index over them:
create unique index ind1 on tablea(PartCandidateA, PartCandidateB);