Is it better to have a single column primary key, or use composite primary keys . I have examples below:
create table emp(
subsidiary_id smallint ,
Employee_id int ,
emp_name varchar,
constraint emp_pkey primary key ( subsidiary_id , Employee_id )
Data will be line
subsidiary_id , employee_id
1, 1
1, 2
1, 3
2, 4
3, 5
2, 6
employee id cannot be same for any subsidiary , always unique but on my reports using both the column in where clause as subsdiary_id = 1 and employee_id = 1 or and so on
so want to know to better approach to create primary on employee_id column or both which one is better?
It sounds like a composite primary key is the right solution for you:
CREATE TABLE emp (
subsidiary_id smallint NOT NULL,
employee_id int NOT NULL,
emp_name text,
CONSTRAINT emp_pkey PRIMARY KEY (subsidiary_id, employee_id)
);
The index that is created for this constraint will also be useful for queries where only subsidiary_id appears in the WHERE condition.
Related
I want to create 3 tables and join them with foreign keys. Unfortunately, it doesn't work and I have no idea where is a mistake.
CREATE TABLE Students (
Student_Id SERIAL PRIMARY KEY,
Name VARCHAR (20) NOT NULL,
Surname VARCHAR (30) NOT NULL,
Date_of_Birth DATE NOT NULL,
Phone INT NOT NULL UNIQUE,
Email VARCHAR(225) NOT NULL UNIQUE,
Course_Id INT
FOREIGN KEY (Course_Id) REFERENCES Course (Course_Id)
);
CREATE TABLE Course (
Course_Id SERIAL PRIMARY KEY,
Student_Id INT NOT NULL,
Teacher_Id INT NOT NULL,
Category VARCHAR (30) NOT NULL,
FOREIGN KEY (Student_Id) REFERENCES Students (Student_Id)
);
CREATE TABLE Teachers (
Teacher_Id SERIAL PRIMARY KEY,
Name VARCHAR (20) NOT NULL,
Surname VARCHAR (30) NOT NULL,
Phone INT NOT NULL UNIQUE,
Salary INT NOT NULL,
Course_Id INT NOT NULL,
FOREIGN KEY (Teacher_Id) REFERENCES Course (Teacher_Id)
);
I should create a Foreign Key to join all three tables.
I get this error every time: relation "course" does not exist
I can't find where is the mistake. Please help.
It appears like you are attempting to crate a many-to-many (M:M) between Students and Teachers with Course as the resolution table. You are close, however, your definition sets up bi-directional relationships. This is normally not necessary. Define Students and Teachers without the FK to Course. Then define Course with a FK to each.
create table students (
student_id serial primary key
, name varchar (20) not null
, surname varchar (30) not null
, date_of_birth date not null
, phone int not null unique
, email varchar(225) not null unique
);
create table teachers (
teacher_id serial primary key
, name varchar (20) not null
, surname varchar (30) not null
, phone int not null unique
, salary int not null
);
create table course (
course_id serial primary key
, student_id int not null
, teacher_id int not null
, category varchar (30) not null
, foreign key (student_id) references students (student_id)
, foreign key (teacher_id) references teachers (teacher_id)
, unique (student_id, teacher_id)
);
If you must define bi-directional FK in Students and Teachers then create the tables without the FK then use alter table after Course is defined to add the FK and make them DEFERRABLE INITIALLY DEFERRED. Necessary for eventual Inserts.
alter table teachers add column course_id int references course(course_id) deferrable initially deferred;
alter table students add column course_id int references course(course_id) deferrable initially deferred;
I have these three tables in postgres database:
create table staff (
e_id int primary key,
first_name text,
last_name text,
job text,
branch_name text);
create table temp_staff (
t_id int primary key,
t_first_name text,
t_last_name text,
t_job text );
create table replacements(
e_id int ,
t_id int,
from_date date,
until_date date,
primary key(e_id, t_id, from_date) );
Now I want to add a constraint to the replacements table so you can insert a replacement with e_id and t_id only if e_id exists in the staff table and t_id exists in the temp_staff table.
I tried this:
alter table replacements
add constraint check_existence
check(exists (select 1 from staff where e_id = e_id) and exists (select 1 from temp_staff where t_id = t_id));
And got the error:
ERROR: cannot use subquery in check constraint
Thanks in advance for any help.
I have 4 different tables that are linked to each other in the following way (I only kept the essential columns in each table to emphasise the relationships between them):
create TABLE public.country (
country_code varchar(2) NOT NULL PRIMARY KEY,
country_name text NOT NULL,
);
create table public.address
(
id integer generated always as identity primary key,
country_code text not null,
CONSTRAINT FK_address_2 FOREIGN KEY (country_code) REFERENCES public.country (country_code)
);
create table public.client_order
(
id integer generated always as identity primary key,
address_id integer null,
CONSTRAINT FK_client_order_1 FOREIGN KEY (address_id) REFERENCES public.address (id)
);
create table public.client_order_line
(
id integer generated always as identity primary key,
client_order_id integer not null,
product_id integer not null,
client_order_status_id integer not null default 0,
quantity integer not null,
CONSTRAINT FK_client_order_line_0 FOREIGN KEY (client_order_id) REFERENCES public.client_order (id)
);
I want to get the data in the following way: for each client order line to show the product_id, quantity and country_name(corresponding to that client order line).
I tried this so far:
SELECT country_name FROM public.country WHERE country_code = (
SELECT country_code FROM public.address WHERE id = (
SELECT address_id FROM public.client_order WHERE id= 5
)
)
to get the country name given a client_order_id from client_order_line table. I don't know how to change this to get all the information mentioned above, from client_order_line table which looks like this:
id client_order_id. product_id. status. quantity
1 1 122 0 1000
2 2 122 0 3000
3 2 125 0 3000
4 3 445 0 2000
Thanks a lot!
You need a few join-s.
select col.client_order_id,
col.product_id,
col.client_order_status_id as status,
col.quantity,
c.country_name
from client_order_line col
left join client_order co on col.client_order_id = co.id
left join address a on co.address_id = a.id
left join country c on a.country_code = c.country_code
order by col.client_order_id;
Alternatively you can use your select query as a scalar subquery expression.
I have created an order-column and i want to update the current values that is in that table.
CREATE TABLE public.publishroomcontacts
(
id integer NOT NULL DEFAULT nextval('publishroomcontacts_id_seq'::regclass),
publishroomid integer NOT NULL,
contactorder integer NOT NULL,
CONSTRAINT publishroomcontacts_pkey PRIMARY KEY (id),
CONSTRAINT fk_publishroomcontacts_publishroom_id FOREIGN KEY (publishroomid)
REFERENCES public.publishrooms (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
)
WITH (
OIDS = FALSE
);
What i want to do is to go through all rows, group by publishroomid and add a contactorder.
So if there is 4 contacts with the same publishroomid whey will have contact order 1, 2, 3 and 4.
How do i do this?
EDIT:
There is this field in a table:
room_id INT NOT NULL CONSTRAINT room_id_ref_room REFERENCES room
I have three 2 tables for two kinds of rooms: standard_room and family_room
How to do something like this:
room_id INT NOT NULL CONSTRAINT room_id_ref_room REFERENCES standard_room or family_room
I mean, room_id should reference either standard_room or family_room.
Is it possible to do so?
Here is the pattern I've been using.
CREATE TABLE room (
room_id serial primary key,
room_type VARCHAR not null,
CHECK CONSTRAINT room_type in ("standard_room","family_room"),
UNIQUE (room_id, room_type)
);
CREATE_TABLE standard_room (
room_id integer primary key,
room_type VARCHAR not null default "standard_room",
FOREIGN KEY (room_id, room_type) REFERENCES room (room_id, room_type),
CHECK CONSTRAINT room_type = "standard_room"
);
CREATE_TABLE family_room (
room_id integer primary key,
room_type VARCHAR not null default "family_room",
FOREIGN KEY (room_id, room_type) REFERENCES room (room_id, room_type),
CHECK CONSTRAINT room_type = "family_room"
);
That is, the 'subclasses' point at the super-class, by way of a type descriminator column (such that the pointed to base class is of the correct type, and that primary key of the super class is the same as the child classes.
Here's the same SQL from the accepted answer that works for PostGres 12.8. There's a few issues not only the CREATE_TABLE syntax mistake:
CREATE TABLE room (
room_id serial primary key,
room_type VARCHAR not null,
CONSTRAINT room_in_scope CHECK (room_type in ('standard_room','family_room')),
CONSTRAINT unique_room_type_combo UNIQUE (room_id, room_type)
);
CREATE TABLE standard_room (
room_id integer primary key,
room_type VARCHAR not null default 'standard_room',
CONSTRAINT roomid_std_roomtype_fk FOREIGN KEY (room_id, room_type) REFERENCES public."room" (room_id, room_type),
CONSTRAINT std_room_constraint CHECK (room_type = 'standard_room')
);
CREATE TABLE family_room (
room_id integer primary key,
room_type VARCHAR not null default 'family_room',
CONSTRAINT roomid_fam_roomtype_fk FOREIGN KEY (room_id, room_type) REFERENCES "room" (room_id, room_type),
CONSTRAINT fam_room_constraint CHECK (room_type = 'family_room')
);
NOTE: The SQL above uses constraints to enforce the child room_type values default to the parent tables' room_type values: 'standard_room' or 'family_room'.
PROBLEM: Since the child tables Primary Key's expect either the standard and family room Primary Key that means you can't insert more than one record in thsee two child tables.
insert into room (room_type) VALUES ('standard_room'); //Works
insert into room (room_type) values ('family_room'); //Works
insert into standard_room (room_id,pictureAttachment) VALUES (1,'Before Paint'); //Works
insert into standard_room (room_id,pictureAttachment) VALUES (1,'After Paint'); //Fails
insert into standard_room (room_id,pictureAttachment) VALUES (1,'With Furniture');
insert into family_room (room_id,pictureAttachment) VALUES (2, 'Beofre Kids'); //Works
insert into family_room (room_id,pictureAttachment) VALUES (2,'With Kids'); //Fails
To make the tables accept > 1 row you have to remove the Primary Keys from the 'standard_room' and 'family_room' tables which is BAD database design.
Despite 26 upvotes I will ping OP about this as I can see the answer was typed free hand.
Alternate Solutions
For smallish tables with less than a handful of variations a simple alterative is a single table with Bool columns for different table Primary Key fields.
Single Table "Room"
Id
IsStandardRoom
IsFamilyRoom
Desc
Dimensions
1
True
False
Double Bed, BIR
3 x 4
2
False
True
3 Set Lounge
5.5 x 7
SELECT * FROM Room WHERE IsStdRoom = true;
At the end of the day, in a relational database it's not very common to be adding Room Types when it involves creating the necessary related database tables using DDL commands (CREATE, ALTER, DROP).
A typical future proof database design allowing for more Tables would look something like this:
Multi Many-To-Many Table "Room"
Id
TableName
TableId
1
Std
8544
2
Fam
236
3
Std
4351
Either Standard or Family:
select * from standard_room sr where sr.room_id in
(select TableId from room where TableName = 'Std');
select * from family_room fr where fr.room_id in
(select id from room where TableName = 'Fam');
Or both:
select * from standard_room sr where sr.room_id in
(select TableId from room where TableName = 'Std')
UNION
select * from family_room fr where fr.room_id in
(select id from room where TableName = 'Fam');
Sample SQL to demo Polymorphic fields:
If you want to have different Data Types in the polymorphic foreign key fields then you can use this solution. Table r1 stores a TEXT column, r2 stores a TEXT[] Array column and r3 a POLYGON column:
CREATE OR REPLACE FUNCTION null_zero(anyelement)
RETURNS INTEGER
LANGUAGE SQL
AS $$
SELECT CASE WHEN $1 IS NULL THEN 0 ELSE 1 END;
$$;
CREATE TABLE r1 (
r1_id SERIAL PRIMARY KEY
, r1_text TEXT
);
INSERT INTO r1 (r1_text)
VALUES ('foo bar'); --TEXT
CREATE TABLE r2 (
r2_id SERIAL PRIMARY KEY
, r2_text_array TEXT[]
);
INSERT INTO r2 (r2_text_array)
VALUES ('{"baz","blurf"}'); --TEXT[] ARRAY
CREATE TABLE r3 (
r3_id SERIAL PRIMARY KEY
, r3_poly POLYGON
);
INSERT INTO r3 (r3_poly)
VALUES ( '((1,2),(3,4),(5,6),(7,8))' ); --POLYGON
CREATE TABLE flex_key_shadow (
flex_key_shadow_id SERIAL PRIMARY KEY
, r1_id INTEGER REFERENCES r1(r1_id)
, r2_id INTEGER REFERENCES r2(r2_id)
, r3_id INTEGER REFERENCES r3(r3_id)
);
ALTER TABLE flex_key_shadow ADD CONSTRAINT only_one_r
CHECK(
null_zero(r1_id)
+ null_zero(r2_id)
+ null_zero(r3_id)
= 1)
;
CREATE VIEW flex_key AS
SELECT
flex_key_shadow_id as Id
, CASE
WHEN r1_id IS NOT NULL THEN 'r1'
WHEN r2_id IS NOT NULL THEN 'r2'
WHEN r3_id IS NOT NULL THEN 'r3'
ELSE 'wtf?!?'
END AS "TableName"
, CASE
WHEN r1_id IS NOT NULL THEN r1_id
WHEN r2_id IS NOT NULL THEN r2_id
WHEN r3_id IS NOT NULL THEN r3_id
ELSE NULL
END AS "TableId"
FROM flex_key_shadow
;
INSERT INTO public.flex_key_shadow (r1_id,r2_id,r3_id) VALUES
(1,NULL,NULL),
(NULL,1,NULL),
(NULL,NULL,1);
SELECT * FROM flex_key;