I have a Postgres database with table t and column fk_c. I want to convert the column to a foreign key that references c_id in table lookup_c and allows null values. How can I do this?
ALTER TABLE public.t ADD CONSTRAINT "fk_t_c" FOREIGN KEY ("fk_c" ) REFERENCES "public"."lookup_c" ("c_id");
does not work because there rows with null values in column fk_c and I get
ERROR: insert or update on table "t" violates foreign key constraint "fk_t_c"
DETAIL: Key (fk_c)=() is not present in table "lookup_c".
The error message indicates that you have empty strings in that column, not null values.
You need to set those values to null before creating the foreign key:
update t
set fk_c = null
where trim(fk_c) = '';
I'm analyzing a following PostgreSQL schema:
CREATE SEQUENCE ref_email_type_ref_email_type_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
CREATE TABLE ref_email_type
(
ref_email_type_id integer NOT NULL DEFAULT nextval('ref_email_type_ref_email_type_id_seq'::regclass),
description character varying(50) NOT NULL,
CONSTRAINT ref_email_type_pkey PRIMARY KEY (ref_email_type_id)
)
CREATE TABLE email
(
email_id integer NOT NULL DEFAULT nextval('email_email_id_seq'::regclass),
ref_email_type_id integer NOT NULL DEFAULT nextval('email_ref_email_type_id_seq'::regclass),
email_address character varying(100),
CONSTRAINT email_pkey PRIMARY KEY (email_id),
CONSTRAINT email_ref_email_type_id_fkey FOREIGN KEY (ref_email_type_id)
REFERENCES ref_email_type (ref_email_type_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Does it make any sense to have email.ref_email_type_id declared with DEFAULT nextval('email_ref_email_type_id_seq'::regclass) in case of NOT NULL and existing constraint:
CONSTRAINT email_ref_email_type_id_fkey FOREIGN KEY (ref_email_type_id)
REFERENCES ref_email_type (ref_email_type_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
No, that doesn't make any sense at all.
It won't do any harm either, since the default value will probably never be used.
This is my creation script:
CREATE TABLE PERSOANE (
idPers numeric (5)
CONSTRAINT pk_persoane PRIMARY KEY,
NumePren varchar (30)
CONSTRAINT ck_nume CHECK (NumePren=LTRIM(INITCAP(NumePren))),
Loc varchar (30)
CONSTRAINT nn_loc NOT NULL
CONSTRAINT ck_loc CHECK (Loc=LTRIM(INITCAP(Loc))),
Jud varchar (25)
CONSTRAINT nn_jud NOT NULL
CONSTRAINT ck_jud CHECK (Jud=LTRIM(INITCAP(Jud))),
Tel numeric (10)
CONSTRAINT nn_tel NOT NULL,
E_mail varchar(254)
CONSTRAINT nn_e_mail NOT NULL
CONSTRAINT ck_e_mail CHECK (E_mail = LTRIM(E_mail))
);
When I try to insert values in the table I got error: new row for relation "persoane" violates check constraint "ck_jud" Here is my insert script:
INSERT INTO PERSOANE VALUES (11111, 'slimi marius', 'oras', 'judet', 0752361507, 'simic#yahoo.com');
Anyone has any suggestion how to fix this problem?
initcap() will change the first character to uppercase. So your check constraints on the columns numepren, loc and jud require you to enter values where the first character of every word is in uppercase. In the value 'jude' the first character is lowercase, so the check constraint is violated. You need to use 'Jude' instead. This is also true for the other columns you have defined:
INSERT INTO PERSOANE
(idpers, numepren, loc, jud, tel, e_mail)
VALUES
(11111, 'Slimi Marius', 'Oras', 'Judet', 0752361507, 'simic#yahoo.com');
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;
How to provide primary key for multiple column in a single table using PostgreSQL?
Example:
Create table "Test"
(
"SlNo" int not null primary key,
"EmpID" int not null, /* Want to become primary key */
"Empname" varchar(50) null,
"EmpAddress" varchar(50) null
);
Note: I want to make "EmpID" also a primary key.
There can only be one primary key per table - as indicated by the word "primary".
You can have additional UNIQUE columns like:
CREATE TABLE test(
sl_no int PRIMARY KEY, -- NOT NULL due to PK
emp_id int UNIQUE NOT NULL,
emp_name text,
emp_addr text
);
Columns that are (part of) the PRIMARY KEY are marked NOT NULL automatically.
Or use a table constraint instead of a column constraint to create a single multicolumn primary key. This is semantically different from the above: Now, only the combination of both columns must be unique, each column can hold duplicates on its own.
CREATE TABLE test(
sl_no int, -- NOT NULL due to PK below
emp_id int , -- NOT NULL due to PK below
emp_name text,
emp_addr text,
PRIMARY KEY (sl_no, emp_id)
);
Multicolumn UNIQUE constraints are possible, too.
Aside: Don't use CaMeL-case identifiers in Postgres. Use legal, lower-case identifiers so you never have to use double-quotes. Makes your life easier. See:
Are PostgreSQL column names case-sensitive?
In case you want to specify the name of the primary key constraint:
CREATE TABLE test(
sl_no int not null,
emp_id int not null,
emp_name text,
emp_addr text,
constraint pk_test primary key (sl_no, emp_id)
);
Source: https://www.postgresqltutorial.com/postgresql-primary-key/