Why this UNIQUE constraint is needed? - sql

I am trying to design schema in PostgreSQL that will contain two tables cross referencing each other. Yet if do not add redundant UNIQUE constrain (see code below) i am getting error: ERROR: there is no unique constraint matching given keys for referenced table "nodes".
So my question is: why this extra unique constraint is needed and is there a way to avoid it creation? (to reduce runtime overhead).
CREATE TABLE objects (
object_id serial NOT NULL PRIMARY KEY,
root_node integer
);
CREATE TABLE nodes (
node_id integer NOT NULL PRIMARY KEY,
object_id integer REFERENCES objects
);
ALTER TABLE objects
ADD CONSTRAINT root_node_fkey
FOREIGN KEY (root_node) REFERENCES nodes(node_id);
-- Why this constaint is needed? Since node_id is primary key this combination should be already UNIQUE
ALTER TABLE nodes ADD CONSTRAINT node_id_object_id_unique UNIQUE (node_id, object_id);
ALTER TABLE objects
ADD CONSTRAINT objects_nodes_fkey
FOREIGN KEY (object_id, root_node)
REFERENCES nodes (object_id, node_id);

https://www.postgresql.org/docs/current/static/ddl-constraints.html says:
5.3.5. Foreign Keys:
. . .
A foreign key must reference columns that either are a primary key or form a unique constraint. This means that the referenced columns always have an index (the one underlying the primary key or unique constraint); so checks on whether a referencing row has a match will be efficient.
https://mariadb.com/kb/en/sql-99/constraint_type-foreign-key-constraint/ says:
A FOREIGN KEY Constraint is either a < Table Constraint> or a and defines a rule that constrains a foreign key to values that match only those values contained in a referenced unique key.
Re your comment:
The idea is that each object will have collections of nodes associated with it and only one of nodes could be the root-node.
ALTER TABLE objects
ADD COLUMN root_node_id integer,
ADD CONSTRAINT objects_nodes_fkey
FOREIGN KEY (root_node_id)
REFERENCES nodes (node_id);
That way each object references exactly one node. Admittedly, constraint doesn't strictly enforce that an object references a node that references the same object.
If you want that level of enforcement, you'll have to create the unique constraint you were asking if you had to.

Related

Efficiently enforcing a 1:1 relationship between two rows with foreign key constraints without creating redundant unique indexes

I have two PostgreSQL tables designed in the following way:
create type content_owner as enum (
'document',
'task'
);
create table content (
id serial not null primary key,
owner content_owner not null,
owner_document_id int references document(id) deferrable initially deferred,
owner_task_id int references task(id) deferrable initially deferred,
-- ...
constraint collab_content_owner_document
check (owner_document_id is null or (owner = 'document' and owner_document_id is not null)),
constraint collab_content_owner_task
check (owner_task_id is null or (owner = 'task' and owner_task_id is not null))
);
create table document (
id serial not null primary key,
content_id int not null references content(id),
-- ...
);
create table task (
id serial not null primary key,
content_id int not null references content(id),
-- ...
);
I want to enforce a 1:1 relationship at the database level for the document<->content relationship and the task<->content relationship.
Adding the following constraints accomplishes that:
alter table collab_content add foreign key (owner_document_id, id) references document (id, content_id) deferrable initially deferred;
alter table collab_content add foreign key (owner_task_id, id) references task (id, content_id) deferrable initially deferred;
alter table document add foreign key (content_id, id) references collab_content (id, owner_document_id);
alter table task add foreign key (content_id, id) references collab_content (id, owner_task_id);
Since I’m saying the ID pair should reference the same ID pair in the other table for both directions. However, this also requires me to create the following indexes:
alter table document add unique (id, content_id);
alter table task add unique (id, content_id);
alter table collab_content add unique (id, owner_document_id);
alter table collab_content add unique (id, owner_task_id);
These indexes feel pretty redundant given that there’s already a primary key on the id columns for these tables. It feels like PostgreSQL should be smart enough to be able to use the existing primary key constraint to make sure the foreign key constraints are met. Ideally I wouldn’t create a second, redundant, index on these tables for the purpose of these foreign key constraints.
Is there a way for me to avoid creating new unique indexes and instead tell PostgreSQL to only lookup the unique ID when resolving the foreign key?
Will PostgreSQL detect that these unique indexes are redundant (because the first column is the primary key) and not materialize a new index on disk for their purpose?
Is there a better way to enforce this constraint?
Two-way linking like this is a recipe for headaches. I recommend avoiding reference cycles if you can. In your case, the simplest way to store this information is to relax the constraint that there cannot be a content without a document or a task. Ask yourself, how might such a situation occur, how else could it be avoided, and what damage might it cause if it happens?
If we can remove that constraint, then we can have a very simple structure where document and task each have a content_id foreign key, and a unique index on it to ensure that no two documents have the same content.
If we can't remove that constraint, then the answers to your questions are:
There is no way to avoid creating those new unique indexes for the foreign keys. Foreign keys must have matching unique indexes.
Postgres will not detect that these indexes are redundant, and they will indeed be materialized and take up space.

How to manage postgresql foreign keys?

I need some advice on SQL structure on Postgresql.
I have those two tables :
DROP TABLE IF EXISTS "public"."attribute_value";
CREATE TABLE "public"."attribute_value"
(
"id" INTEGER NOT NULL,
"attribute_id" INTEGER NOT NULL,
"value" CHARACTER VARYING(100) NULL
);
--*****************************************************
DROP TABLE IF EXISTS "public"."product_attribute";
CREATE TABLE "public"."product_attribute"
(
"product_id" INTEGER NOT NULL,
"attribute_value_id" INTEGER NOT NULL,
"attribute_id" INTEGER NOT NULL
);
I added no constraints on purpose.
I need a foreign key on the child table product_attribute.attribute_value_id referencing the parent table attribute_value.id. The best practice is to create a primary key on the field attribute_value.id (maybe with a sequence), or to CREATE UNIQUE INDEX on attribute_value.id ?
I first thought indexes were only special lookup tables that the database search engine can use to speed up data retrieval. But when I played with foreign keys, I found that creating an unique index allowed me to avoid error "there is no unique constraint matching given keys for referenced table blablabla" because a foreign key is not supposed to point to a non unique value. Should indexes be used to create foreign keys then ?
I also need a foreign key on the child table product_attribute.attribute_id referencing parent table attribute_value.attribute_id. The problem is that attribute_value.attribute_id is not unique. But all the rows in product_attribute.attribute_id must not take any value out of attribute_value.attribute_id's possible values. How should I do ?
Every table should have a primary key. Don't join the legion of people who complain about duplicate rows in their tables.
So make id the primary key of attribute_value, then you can create a foreign key constraint.
Constraints are implemented by unique indexes, so technically they are almost the same. Still you need a constraint and not just a unique index as the target of a foreign key constraint.
About attribute_id: that should not be a foreign key constraint between the two tables from your question, but both tables should have a foreign key referencing yet another table (attribute?).

How to reference a postgres table with multiple primary keys

How could I create a xref table with a reference to this table?
CREATE TABLE reviewer (
screen_name integer,
identity_provider text
CONSTRAINT identity PRIMARY KEY (screen_name, identity_provider)
);
This is what I've tried so far:
CREATE TABLE business_reviewer_xref (
reviewer_screen_name integer,
reviewer_identity_provider text,
CONSTRAINT reviewer_identity UNIQUE (reviewer_screen_name, reviewer_identity_provider),
reviewer_identity REFERENCES reviewer(identity)
);
CREATE TABLE business_reviewer_xref (
reviewer_screen_name integer,
reviewer_identity constraint REFERENCES reviewer(identity)
);
CREATE TABLE business_reviewer_xref (
reviewer_screen_name integer,
reviewer_identity REFERENCES reviewer(identity)
);
A table cannot have multiple primary keys. It can only have one or none. But a primary key can of course consist of more than one column (but at least of one of course). To reference such a multi column primary key, you need a corresponding column in the referencing table for each column in the primary key of the referenced column.
To define a foreign key constraint, list the corresponding columns in the referencing table in the same order as their counterparts occur in the primary key constraint in the referenced table in the defining tuple of the foreign key. Also keep the order in the definition of the referenced columns' tuple.
In your case:
CREATE TABLE business_reviewer_xref
(reviewer_screen_name integer,
reviewer_identity text,
FOREIGN KEY (reviewer_screen_name,
reviewer_identity)
REFERENCES reviewer
(screen_name,
identity));
Note that a foreign key constraint, in contrast to a primary key constraint, doesn't implicitly set a unique constraint. If you want uniqueness too, you'd have to define another constraint for that.

SQL - Unique Key, Primary Key & Foreign Key

What are the differences between Unique Key, Primary Key and Foreign Key with respect to concept of SQL?
How they are different from each other?
A PRIMARY Key and UNIQUE Key constraints both are similar and it provide unique enforce uniqueness of the column on which they are defined.
Primary Key
Primary key cannot have a NULL value.
Each table can have only one primary key.
By default, Primary key is clustered index and data in the database table is physically organized in the sequence of clustered index.
Primary key can be related with another table's as a Foreign Key.
We can generated ID automatically with the help of Auto Increment field. Primary key supports Auto Increment value.
Unique Key
Unique Constraint may have a NULL value.
Each table can have more than one Unique Constraint.
By default, Unique key is a unique non-clustered index.
Unique Constraint can not be related with another table's as a Foreign Key.
Unique Constraint doesn't supports Auto Increment value.
Foreign Key
Foreign key is a field in the table that is primary key in another table.
Foreign key can accept multiple null value.
Foreign key do not automatically create an index, clustered or non-clustered. You can manually create an index on foreign key.
We can have more than one foreign key in a table.
There are actual advantages to having a foreign key be supported with a clustered index, but you get only one per table. What's the advantage? If you are selecting the parent plus all child records, you want the child records next to each other. This is easy to accomplish using a clustered index.
Having a null foreign key is usually a bad idea. In the example below, the record in [dbo].[child] is what would be referred to as an "orphan record". Think long and hard before doing this.
Note: we use constraint for enforce data integrity
Primary Key
1)can't insert null value
2) one table have one primary key
Unique key
1) insert null value one at time
2)one table have multiple unique key
3) you can refereed as candidate key also
foreign key
1) maintain the relationship between two table and also multiple
Note: without any constraint you get data in multiple table but you can not get data peoperly
A note about Unique key
The parent table in a Primary Key-Foreign Key relation is normally called as Primary Key table but PK is not mandatory in a parent table. A unique key/constraint in parent table is sufficient. As PK is always unique, it is often used as foreign key in another table. see this SO post

What is difference between foreign key and reference key?

I am very confused about those two terms. Are they the same or different?
Some books and people say they are the same and others say they are different.
I tried but couldn't find a conclusive answer.
I am supposing that you are talking about using the REFERENCES where the FOREIGN KEY keyword is not used when constraining a column inline, which is called a column-level foreign key constraint, eg.
author_id INTEGER REFERENCES author(id)
... instead of the table-level foreign key constraint, which is placed after the column declarations ...
author_id INTEGER,
FOREIGN KEY(author_id) REFERENCES author(id)
The answer is, that it is simply shorthand syntax for the same thing. The main concern when altering between the two should be readability.
For more advanced use, it might be relevant that only table-level foreign key constraints can describe constraints on multiple keys at once, where all must be present in the referenced table.
Do note that MySQL 'parses but ignores “inline REFERENCES specifications” (as defined in the SQL standard) where the references are defined as part of the column specification', meaning that only the table-level foreign key constraint will work.
Both Postgres and Microsoft's SQL Server respect both column- and table-level foreign key constraints.
A foreign key must refer to a primary key.
When using REFERENCES constraint simply, then it isn't necessary that the referenced key be a primary key.
"Reference key" isn't a normal technical term in relational modeling or in SQL implementation in US English.
A foreign key "references" a key in some other table; could that be where the confusion comes from?
You don't really call something a reference key... They are the same thing... you might see the word references used for example in sqlite: you might use syntax like this to start a db of authors and books. This lets you show that one author can have many books. This tells the db that the books.author_id (defined a couple of lines up) references author.id
CREATE TABLE 'author' (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
firstname varchar(255)
lastname varchar(255)
);
CREATE TABLE 'books' (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
author_id INTEGER,
title varchar(255),
published date,
FOREIGN KEY(author_id) REFERENCES author(id)
);
In terms of standard SQL, both result in a foreign key constraint.
One form is a table constraint, meaning it can apply to one or more columns. You would need this to reference a table that has a multi-column primary key:
CREATE TABLE child (
id int PRIMARY KEY,
parent_id int,
date date,
FOREIGN KEY (parent_id, date) REFERENCES parent(id, date)
);
The other form is a column constraint, meaning it can only apply to the single column it is defined with. It cannot be used to reference a table with a multi-column primary key.
CREATE TABLE child (
id int PRIMARY KEY,
parent_id int REFERENCES parent(id)
);
The above syntax works exactly the same as if you declared a table constraint for a single column (supposing the RDBMS supports this type of column constraint), as follows:
CREATE TABLE child (
id int PRIMARY KEY,
parent_id int,
FOREIGN KEY (parent_id) REFERENCES parent(id)
);
It frequently causes confusion for users of MySQL and its InnoDB storage engine, that the latter column-constraint style is not supported. You must define a table-level constraint for a foreign key, even if it is a single-column constraint. This has been a strange behavior of MySQL since its earliest days, that some constraint syntax is valid, but results in no constraint. See discussion here: https://bugs.mysql.com/bug.php?id=17943
The only and most important difference between the two keywords 'FOREIGN KEY" and "REFERENCES" keywords is though both of them make the data to be child data of the parent table, the "FOREIGN KEY" is used to create a table level constraint whereas REFERENCES keyword can be used to create column level constraint only. Column level constraints can be created only while creating the table only. But table level constraints can be added using ALTER TABLE command.
Perhaps you are using the term "reference key" somewhat loosely?
A foreign key value in one row is said to "reference" the row that contains the corresponding key value. Note the word "reference" in the prior sentence is a verb, so we may say we have a referencing foreign key value and a referenced key value.
Although it is the key values, rather than the table key constraint, that is being referenced, I suppose loosely speaking we could say "referenced key" to mean the rows that comprise the values that may potentially be referenced. I then see how "referenced key" could become "referenced key" but not belie its origin.
There are 2 ways to declare a foreign key(s):
if the foreign key is a SINGLE attribute:
REFERENCES ()
if foreign keys are a LIST of attributes
FOREIGN KEY () REFERENCES
A foreign key "references" a key in some other table. That key in some other table is called Referenced key. You'll probably hear a lot about this if you're using Graphic feature on phpmyadmin.
The Reference Key is the primary key that is referenced in the other table.
On the other hand, Foreign Key is how you link the second table to the primary tables Primary Key (or Reference Key).