Cannot add Foreign Key on tables in DashDB / DB2 on Bluemix - db2

When I create a table in DashDB (DB2) on Bluemix like this:
CREATE TABLE DEPARTMENT (
depname CHAR (10) UNIQUE NOT NULL ,
phone INTEGER
) ;
ALTER TABLE DEPARTMENT ADD CONSTRAINT DEPARTMENT_PK PRIMARY KEY ( depname ) ;
CREATE TABLE EMPLOYEE (
"EmpNr" NUMERIC (3) UNIQUE NOT NULL ,
empname CHAR (20) ,
depname CHAR (10) ,
EMPLOYEE2_title CHAR (20)
);
ALTER TABLE EMPLOYEE ADD CONSTRAINT EMPLOYEE_PK PRIMARY KEY ( "EmpNr" ) ;
ALTER TABLE EMPLOYEE ADD CONSTRAINT EMPLOYEE_DEPARTMENT_FK FOREIGN KEY (depname ) REFERENCES DEPARTMENT ( depname ) ;
Bluemix disallows adding a Foreign Key constraint for this table type.

When you look at the documentation for dashDB (not DB2) you will notice that foreign keys can be created. However, a table by default is created column-organized. Only non-enforced referential constraints are supported. In your example you would need to add NOT ENFORCED to your statement:
ALTER TABLE EMPLOYEE
ADD CONSTRAINT EMPLOYEE_DEPARTMENT_FK FOREIGN KEY (depname )
REFERENCES DEPARTMENT ( depname ) NOT ENFORCED;

By default on CREATE a DashDB table on Bluemix is 'organized by column'...
https://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.admin.dbobj.doc/doc/c0060592.html
This will also disallow adding Foreign Key constraints for this table type.
To add FKs add ORGANIZE BY ROW to your CREATE TABLE statement:
CREATE TABLE DEPARTMENT (
depname CHAR (10) UNIQUE NOT NULL ,
phone INTEGER
) ORGANIZE BY ROW;
ALTER TABLE DEPARTMENT
ADD CONSTRAINT DEPARTMENT_PK PRIMARY KEY ( depname ) ;
CREATE TABLE EMPLOYEE (
"EmpNr" NUMERIC (3) UNIQUE NOT NULL ,
empname CHAR (20) ,
depname CHAR (10) ,
EMPLOYEE2_title CHAR (20)
) ORGANIZE BY ROW;
ALTER TABLE EMPLOYEE
ADD CONSTRAINT EMPLOYEE_PK PRIMARY KEY ( "EmpNr" ) ;
ALTER TABLE EMPLOYEE
ADD CONSTRAINT EMPLOYEE_DEPARTMENT_FK FOREIGN KEY (depname )
REFERENCES DEPARTMENT ( depname ) ;

Related

Does postgresql require unique constraint names when defining FOREIGN KEYS

Here is my schema for database tables:
CREATE TABLE users (
user_id INTEGER NOT NULL,
device_id INTEGER,
user_points INTEGER,
cookie VARCHAR,
PRIMARY KEY (user_id)
);
CREATE TABLE admins (
admin_id INTEGER NOT NULL,
username VARCHAR,
password VARCHAR
);
CREATE TABLE admin_adventure (
adventure_id INTEGER NOT NULL,
admin_id INTEGER,
PRIMARY KEY (adventure_id)
);
CREATE TABLE adventures (
adventure_id INTEGER NOT NULL,
prize_id INTEGER,
novella_id INTEGER,
PRIMARY KEY (adventure_id)
);
Here I'm trying to define FOREIGN KEYS:
ALTER TABLE admin_adventure ADD FOREIGN KEY ( admin_id ) REFERENCES admins ( admin_id );
ALTER TABLE admin_adventure ADD FOREIGN KEY ( adventure_id ) REFERENCES adventures ( adventure_id );
And here is the error I get when trying to migrate with Flyway:
ERROR: there is no unique constraint matching given keys for
referenced table "admins"
Can someone explain what I'm doing wrong and why I get this error?
The error message is complaining that you are trying to link a foreign key to a column in another table which is not unique (e.g. a primary key). Try making the admin_id column in the admins table a primary key:
CREATE TABLE admins (
admin_id INTEGER NOT NULL,
username VARCHAR,
password VARCHAR,
PRIMARY KEY (admin_id)
);
Like the documentation says:
A foreign key must reference columns that either are a primary key or form a unique constraint.
So you must either
ALTER TABLE admins ADD PRIMARY KEY (admin_id);
or
ALTER TABLE admins ADD UNIQUE (admin_id);

update order column based on groupby postgresql

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:

postgresql key contraint fault

I try to create code for postgresql
CREATE TABLE kaart (
kaartid integer NOT NULL,
naam character varying,
saldo real,
kaarthouderid integer
);
CREATE TABLE kaart_product (
kaartkaartid integer,
productid2 integer
);
CREATE TABLE kaarthouder (
id integer NOT NULL,
naam character varying(255),
naw character varying(255),
geslacht "char"
);
CREATE TABLE product (
naam character varying,
id integer NOT NULL
);
ALTER TABLE ONLY kaart
ADD CONSTRAINT kaart_pkey PRIMARY KEY (kaartid);
ALTER TABLE ONLY kaarthouder
ADD CONSTRAINT kaarthouder_pkey PRIMARY KEY (id);
ALTER TABLE ONLY product
ADD CONSTRAINT product_pkey PRIMARY KEY (id);
ALTER TABLE ONLY kaart
ADD CONSTRAINT kaartco FOREIGN KEY (kaartid) REFERENCES kaarthouder(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY kaart_product
ADD CONSTRAINT kaartkaartidco FOREIGN KEY (kaartkaartid) REFERENCES kaart(kaartid) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY kaart_product
ADD CONSTRAINT productidco FOREIGN KEY (kaartkaartid) REFERENCES product(id) ON UPDATE CASCADE ON DELETE CASCADE;
INSERT INTO kaart VALUES (1, 'Sander',50.00 ,1);
INSERT INTO kaart_product VALUES (1,1);
INSERT INTO kaarthouder VALUES (1, 'Sander','test,testing','man');
INSERT INTO product VALUES ('studentenproduct',1);
But whenever i try to run it it gives me this error:
23503: insert or update on table "kaart" violates foreign key constraint "kaartco"
But i really dont know why this happens since it is the same to the other foreign keys that are below it
So can someone help me fix this?
You try to link to a product and a kaart that doesn't exist yet.
Move:
INSERT INTO kaart_product VALUES (1,1);
Two lines down under:
INSERT INTO product VALUES ('studentenproduct',1);
That should do the job for you.
Try to search google for forgein key contstraints and how they work.

"polymorphism" for FOREIGN KEY constraints

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 add a foreign key constraint to same table using ALTER TABLE in PostgreSQL

To create table I use:
CREATE TABLE category
(
cat_id serial NOT NULL,
cat_name character varying NOT NULL,
parent_id integer NOT NULL,
CONSTRAINT cat_id PRIMARY KEY (cat_id)
)
WITH (
OIDS=FALSE
);
ALTER TABLE category
OWNER TO pgsql;
parent_id is a id to another category. Now I have a problem: how to cascade delete record with its children? I need to set parent_id as foreign key to cat_id.
I try this:
ALTER TABLE category
ADD CONSTRAINT cat_cat_id_fkey FOREIGN KEY (parent_id)
REFERENCES category (cat_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
But it falls with:
ERROR: insert or update on table "category" violates foreign key constraint "cat_cat_id_fkey"
DETAIL: Key (parent_id)=(0) is not present in table "category".
The problem you have - what would be the parent_id of a category at the top of the hierarchy?
If it will be null - it will break the NOT NULL constratint.
If it will be some arbitrary number like 0 - it will break the foreign key (like in your example).
The common solution - drop the NOT NULL constratint on the parent_id and set parent_id to null for top categories.
-- create some fake data for testing
--
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE category
(
cat_id serial NOT NULL,
cat_name character varying NOT NULL,
parent_id integer NOT NULL,
CONSTRAINT cat_id PRIMARY KEY (cat_id)
);
INSERT INTO category(cat_name,parent_id)
SELECT 'Name_' || gs::text
, gs % 3
FROM generate_series(0,9) gs
;
-- find the records with the non-existing parents
SELECT ca.parent_id , COUNT(*)
FROM category ca
WHERE NOT EXISTS (
SELECT *
FROM category nx
WHERE nx.cat_id = ca.parent_id
)
GROUP BY ca.parent_id
;
-- if all is well: proceed
-- make parent pointer nullable
ALTER TABLE category
ALTER COLUMN parent_id DROP NOT NULL
;
-- set non-existing parent pointers to NULL
UPDATE category ca
SET parent_id = NULL
WHERE NOT EXISTS (
SELECT *
FROM category nx
WHERE nx.cat_id = ca.parent_id
)
;
-- Finally, add the FK constraint
ALTER TABLE category
ADD CONSTRAINT cat_cat_id_fkey FOREIGN KEY (parent_id)
REFERENCES category (cat_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
;
This is quite simple.
Here the foreign key parent_id refers to cat_id.
Here a record with parent_id=0 exists but not a record with cat_id=0.