Unique constraint on 2 fields with special logic in Postgres - postgresql

I have a table with 3 columns Country,PersonID, Comments.
I want to add a database constraint which will enable multiple records for a PersonID in the same country, but will block adding the same PersonId to multiple countries.
Is it possible?

If you can add another table to the mix, this should be doable:
CREATE TABLE PersonCountries (
PersonID char(7) not null,
Country char(3) not null,
constraint UQ_PersonCountries_Persons UNIQUE (PersonID),
constraint UQ_PersonCountries_XRef UNIQUE (PersonID,Country)
)
You can then have a foreign key from your table to this table on both the PersonID and Country columns. Since PersonID, by itself in the above table has to be unique, you know that each person is only linked to one country. The second unique constraint is to allow the foreign key constraint to be created.

Related

Citus: How can I add self referencing table in distributed tables list

I'm trying to run create_distributed_table for tables which i need to shard and almost all of the tables have self relation ( parent child )
but when I run SELECT create_distributed_table('table-name','id');
it throws error cannot create foreign key constraint
simple steps to reproduce
CREATE TABLE TEST (
ID TEXT NOT NULL,
NAME CHARACTER VARYING(255) NOT NULL,
PARENT_ID TEXT
);
ALTER TABLE TEST ADD CONSTRAINT TEST_PK PRIMARY KEY (ID);
ALTER TABLE TEST ADD CONSTRAINT TEST_PARENT_FK FOREIGN KEY (PARENT_ID) REFERENCES TEST (ID);
ERROR
citus=> SELECT create_distributed_table('test','id');
ERROR: cannot create foreign key constraint
DETAIL: Foreign keys are supported in two cases, either in between two colocated tables including partition column in the same ordinal in the both tables or from distributed to reference tables
For the time being, it is not possible to shard a table on PostgreSQL without dropping the self referencing foreign key constraints, or altering them to include a separate and new distribution column.
Citus places records into shards based on the hash values of the distribution column values. It is most likely the case that the hashes of parent and child id values are different and hence the records should be stored in different shards, and possibly on different worker nodes. PostgreSQL does not have a mechanism to create foreign key constraints that reference records on different PostgreSQL clusters.
Consider adding a new column tenant_id and adding this column to the primary key and foreign key constraints.
CREATE TABLE TEST (
tenant_id INT NOT NULL,
id TEXT NOT NULL,
name CHARACTER VARYING(255) NOT NULL,
parent_id TEXT NOT NULL,
FOREIGN KEY (tenant_id, parent_id) REFERENCES test(tenant_id, id),
PRIMARY KEY (tenant_id, id)
);
SELECT create_distributed_table('test','tenant_id');
Note that parent and child should always be in the same tenant for this to work.

Is there a way to create a unique constraint for 2 tables?

Currently, I have the following 3 tables.
CREATE TABLE customer (
id SERIAL PRIMARY KEY
);
CREATE TABLE google_subscription (
fk_customer_id INTEGER NOT NULL UNIQUE,
CONSTRAINT fk_customer_id_constraint
FOREIGN KEY(fk_customer_id)
REFERENCES customer(id)
ON DELETE RESTRICT
);
CREATE TABLE apple_subscription (
fk_customer_id INTEGER NOT NULL UNIQUE,
CONSTRAINT fk_customer_id_constraint
FOREIGN KEY(fk_customer_id)
REFERENCES customer(id)
ON DELETE RESTRICT
);
google_subscription is having fk_customer_id referencing to customer table id.
apple_subscription is having fk_customer_id referencing to customer table id.
I was wondering, is it ever possible to create a constraint, such that customer table id, will only be found in either google_subscription or apple_subscription, but NOT both?
No that is not possible. A constraint cannot span across multiple tables. Instead you can have another table subscriptions where you can have a unique index on customer(id). Or you can have another column in customer table which will hold only 1 subscription at a time.

How to ensure that two columns in different tables have the same values

What T-SQL DDL is required to create a constraint that ensures that the values in a column in one table are the same as the values in a column in a different table?
I want to do this without using a PK-FK relationship.
The T-SQL DDL at the end of this post is an example of the generic problem that I'm trying to solve.
In this example, I want to know how to add an equality constraint between the two tables that ensures that the set of values in the column:
"PersonMayDriveCar.personName"
is always equal to the set of values in the column
"DriverLicense.personName"
CREATE SCHEMA "Equality Constraint"
GO
CREATE TABLE "Equality Constraint".PersonMayDriveCar
(
carVin nchar(4000) NOT NULL,
personName nchar(70) NOT NULL,
CONSTRAINT PersonMayDriveCar_PK PRIMARY KEY(personName, carVin)
)
GO
CREATE TABLE "Equality Constraint".DriverLicense
(
driverLicenseNr int NOT NULL,
personName nchar(70) NOT NULL,
CONSTRAINT DriverLicense_PK PRIMARY KEY(driverLicenseNr),
CONSTRAINT DriverLicense_UC UNIQUE(personName)
)
GO
I see that you want to maintain referential integrity between the two tables without using a foreign key.
Based on my past experience, I solved such an issue using a trigger.
So you can create a trigger on the DriverLicense table which ensures that any insert or update into the DriverLicense table will be rolled back if the inserted driverLicenseNr doesn't exist in the PersonMayDriveCar table.
You can go through this for a full example:
https://www.mssqltips.com/sqlservertip/4242/sql-server-referential-integrity-without-foreign-keys/
Adhere to convention:
Use an FK. It’s that simple.
Don’t link these table together with an FK, because they are both child tables of ...
Create a person table, which is the parent of the other two tables
Try this:
Person
- id (PK)
- name
- other columns
PersonMayDriveCar
- person_id (FK to person)
- other columns
DriverLicense
- person_id (FK to person)
- other columns

Create a foreign key from two different tables

I have two tables in PostgreSQL:
create Table student (
studentID integer primary key,
studentname text
);
create Table courses (
courseID text primary key,
schoolname text
);
I want to create a third table schoolstudent that has a foreign key (studentID, schoolname) where studentID references the primary key of the student table and schoolname references the schoolname key in the courses table.
How can I create a foreign key from two different tables in PostgreSQL 9.4 or 9.5?
A FK constraint requires a UNIQUE or PK constraint on the target column(s), which schoolname obviously cannot provide. You need another table with unique rows per school:
CREATE TABLE school(
school_id serial PRIMARY KEY,
schoolname text NOT NULL
);
CREATE TABLE student(
student_id serial PRIMARY KEY,
studentname text
);
CREATE TABLE schoolstudent(
school_id int REFERENCES school,
student_id int REFERENCES student,
PRIMARY KEY (school_id, student_id)
);
CREATE TABLE course(
course_id text PRIMARY KEY,
school_id int REFERENCES school
);
Using short syntax for foreign key constraints. Details in the manual.
How to implement a many-to-many relationship in PostgreSQL?
If you really need schoolname in the schoolstudent table (I seriously doubt that, looks like a design error), you can just add it. To enforce referential integrity you can include it in the foreign key, but you need a (redundant) matching UNIQUE constraint on school(school_id, schoolname), too.
CREATE TABLE schoolstudent(
school_id int,
student_id int REFERENCES student,
schoolname text,
PRIMARY KEY (school_id, student_id),
CONSTRAINT schoolstudent_combo_fk FOREIGN KEY (school_id, schoolname)
REFERENCES school (school_id, schoolname) ON UPDATE CASCADE
);
Using explicit syntax in this case. And I suggest to cascade updates.
Or if schoolname is actually guaranteed to be UNIQUE (again, my doubts) you can replace school_id completely and just use schoolname as PK and FK column. Long text columns are not very efficient for the purpose, though - if performance matters. And schoolnames change, which is not ideal for PK columns.
You still need a separate school table in any case.
you can set many to many relation only if both the fields are Unique(probably Primary Keys).if above condition is Fulfilled you can use
CREATE TABLE Schoolstudent(
ID INT references student(studentID),
Schoolname CHAR(50) references courses(Schoolname),
);
But schoolname in table courses should be unique or PK.

How to make a Foreigne key that references NOT Primary or Unique column from another table?

I have made classical Employee - Employer table that has one primary key and one foreign key. Foreign key references primary so it is a self referencing table:
CREATE TABLE Worker
(
OIB NUMERIC(2,0),
Name NVARCHAR(10),
Surname NVARCHAR(20),
DateOfEmployment DATETIME2 NOT NULL,
Adress NVARCHAR(20),
City NVARCHAR(10),
SUPERIOR NUMERIC(2,0) UNIQUE,
Constraint PK_Worker PRIMARY KEY(OIB),
CONSTRAINT FK_Worker FOREIGN KEY (Superior) REFERENCES Worker(OIB)
);
Second table that should keep points for all my employees is made like this:
CREATE TABLE Point
(
OIB_Worker NUMERIC(2,0) NOT NULL,
OIB_Superior NUMERIC(2,0) NOT NULL,
Pt_To_Worker tinyint,
Pt_To_Superior tinyint,
Month_ INT NOT NULL,
Year_ INT NOT NULL,
CONSTRAINT FK_Point_Worker FOREIGN KEY (OIB_Worker) References Worker(OIB),
CONSTRAINT FK_Point_Worker_2 FOREIGN KEY (OIB_Superior) References Worker(Superior),
CONSTRAINT PK_Point PRIMARY KEY(OIB_Worker,OIB_Superior,Month_,Year_)
);
It should enable storing grades per month for every employee.
That is, I have two foreign keys, one for Worker.OIB and one for Worker.Superior. Also, I have a composite primary key made of columns Point.OIB_Worker, Point.OIB_superior, Point.Month_ and Point.Year_. Key is composite because it needs to disable entering grades more then once a month.
My question is:
How to make a foreign key from Point to Worker so that any superior can have more then one employee assigned to him?
If you look closely, my implementation works but it can have only one employee per manager.
That is because of a fact that a foreign key has to reference either the primary or the unique column from other table. And my Worker.Superior is UNIQUE, so it can have only unique values (no repetition).
I think many people will find this example interesting as it is a common problem when making a new database.
I think your FK_Point_Worker_2 should also have References Worker(OIB), and you should remove the UNIQUE constraint from Worker.Superior. That way a superior can have more than one worker assigned to him.
Think about it. You have unique constraint on SUPERIOR and you are confused as to why two employees cannot have the same SUPERIOR. That is what a unique constraint does - not allow duplicates.
A FK can only reference a unique column or columns.
A FK_Point_Worker_2 with a References Worker(OIB) does not assure OIB is a Superior.
I would add a unique constraint on Worker on (OIB, SUPERIOR)
and remove the unique constraint on SUPERIOR.
It will always be unique as OIB is unique.
Then have composite FK relationship.
This is an example of a composite FK relationship
ALTER TABLE [dbo].[wfBchFolder] WITH CHECK ADD CONSTRAINT [FK_wfBchFolder_wfBch] FOREIGN KEY([wfID], [bchID])
REFERENCES [dbo].[WFbch] ([wfID], [ID])
GO