Is it possible to reference a foreign key to parent table? - sql

CREATE TABLE Product
(
"Product_id" int,
"Stock_quantity" int,
"Product_name" varchar(50),
"Model" varchar(50),
"Average_rating" float(3),
"RAM" int,
"Color" varchar(20),
"Price" float(10),
PRIMARY KEY ("Product_id")
);
CREATE TABLE Sale
(
"Sale_id" int,
"Sale_date" date,
"Employee_id" int,
"Customer_id" int,
"Product_id" int,
"Product_quantity" int,
"Rating" int,
PRIMARY KEY ("Sale_id"),
FOREIGN KEY("Employee_id") REFERENCES Employee("Employee_id") ,
FOREIGN KEY("Customer_id") REFERENCES Customer("Customer_id") ,
FOREIGN KEY("Product_id") REFERENCES Product("Product_id")
);
CREATE TABLE Computer
(
"Type" varchar(10),
"Processor" varchar(20),
"Monitor_size" int
) inherits(Product);
CREATE TABLE Mobile
(
"Os" varchar(30),
"Screen_size" int
) inherits(Product);
Here is my tables while insertion of sale rows I get this error.
ERROR: insert or update on table "sale" violates foreign key constraint "PK_Product_id"
SQL state: 23503
Detail: Key (Product_id)=(12) is not present in table "product".
It says there is no presence of the row, but I can see them when I view the table:
I inserted every product as computer or mobile not as product.

You are stumbling over one of the many quirks with inheritance: There are no “global” constraints on an inheritance hierarchy, consequently you cannot have a foreign key to a inheritance hierarchy.
The primary key you defined on product does not extend to computer.
Even though a SELECT on product will append the results for the inheritance children as well, these are not part of the table parent and consequently cannot be used as target for the foreign key. The foreign key is between sale and product only, the inheritance children are excluded.
There is no proper way to do this with inheritance.
It is better for computer not to be an inheritance child of product, but to have a foreign key to product (with a unique constraint on it), so that the data for a computer object will be split between the two tables. This is somewhat inconvenient for queries, but you can have a foreign key that way.
Here is an example:
CREATE TABLE product (
product_id integer PRIMARY KEY,
prodname text NOT NULL
);
CREATE TABLE computer (
product_id integer PRIMARY KEY,
processor text NOT NULL,
FOREIGN KEY (product_id) REFERENCES product(product_id)
);
CREATE TABLE sale (
sale_id integer PRIMARY KEY,
product_id integer NOT NULL REFERENCES product(product_id)
);
Now there is no direct foreign key reference from sale to computer, but since the product_id of computer and product is identical, you can always find the unique computer for a sale if it exists:
SELECT p.prodname, c.processor
FROM sale s
JOIN product p USING (product_id)
LEFT JOIN computer c USING (product_id)
WHERE sale_id = 42;
If you have more product “subtypes”, you can add more left joins.

Related

How to connect two fact tables to one dimensional table

I have two fact tables (HistoricTable, ForecastTable). Both tables use a composite key that combines the productID and WeekID together. The ForecastTable has future weekID's & historic has previous ones. I want both of these ProductID's to reference one Dimensional table called ProductTable. How do I connect them?
My assumption would be to create an additional table that queries HistoricTable & ForecastTable via UNION join and have that connected to the ProductTable. Is this logic correct?
Table Image Layout
I want both of these ProductID's to reference one Dimensional table called ProductTable.
You can establish one foreign key between historic and product and another foreign key between forecast and product.
For example:
create table product (
id int primary key not null,
name varchar(10)
);
create table historic (
product_id int not null references product (id),
week_id int not null,
units_sold int,
primary key (product_id, week_id)
);
create table forecast (
product_id int not null references product (id),
week_id int not null,
forecast_units int,
primary key (product_id, week_id)
);

'Nested' foreign key relationship

A Product has a code (and other attributes). A Product is available in various countries, with a different name in each. A Packaged Product is a product in a particular pack size.
An invoice detail line will hold a package_code and a country_code. The permissible countries for a PackagedProduct are restricted by the contents of the ProductCountry table, i.e. the PackagedProduct is only available in a given Country if the Product is available in that Country.
Here's my approach (Country and InvoiceDetail tables omitted):
create table Product (
product_code varchar(15),
strength varchar(10),
-- other Product attributes
constraint pk_Product primary key (product_code)
);
create table ProductCountry (
product_code varchar(15),
country_code char(2),
local_name varchar(100),
constraint pk_ProductCountry primary key (product_code, country_code),
foreign key (product_code) references Product(product_code),
foreign key (country_code) references Country(country_code)
);
create table PackagedProduct (
package_code varchar(20),
product_code varchar(15),
pack_size int,
constraint pk_PackagedProduct primary key (package_code),
foreign key (product_code) references Product(product_code)
);
My problem is that this design does not restrict Invoice Detail records to valid combinations of PackagedProduct and Country.
Can we improve this design?

PostgreSQL: table referencing only part of a composite primary key

I am Creating a movie and tv show streaming service database(for school project) using Postgres 9.6.2. I am running into the following error:
there is no unique constraint matching given keys for referenced table "watchedepisodes"
The TVratings table below will take a tv show, as long as a user has watched at least one episode (that shows up in the WatchedEpisodes table) and allow the user to rate it. Since WatchedEpisodes has a composite primary key of the user id, tv show id, season, and episode, it won't let me just reference from that table a composite key of just uid and tid.
CREATE TABLE WatchedEpisodes (
uid int unique references Users(uid),
tid int,
season int,
episode int,
FOREIGN KEY(tid, season, episode) REFERENCES Episodes(tid, season, episode),
PRIMARY KEY (uid, tid, season, episode)
);
CREATE TABLE TVRatings (
uid int,
tid int,
rating int check (rating > 0 and rating < 6),
FOREIGN KEY(uid, tid) REFERENCES WatchedEpisodes(uid,tid),
PRIMARY KEY(uid, tid)
);
Wondering if there is a way to only reference part of that composite key. These are only two of my tables so if further information is needed, I can add more.
This is actually in the spec, but it's not implemented yet. It's called MATCH PARTIAL
CREATE TABLE foo (
a int,
b int,
c int,
PRIMARY KEY (a,b,c)
);
CREATE TABLE bar (
a int,
b int,
FOREIGN KEY (a,b) REFERENCES foo (a,b) MATCH PARTIAL
);
You can read about it in the docs,
A value inserted into the referencing column(s) is matched against the values of the referenced table and referenced columns using the given match type. There are three match types: MATCH FULL, MATCH PARTIAL, and MATCH SIMPLE (which is the default). MATCH FULL will not allow one column of a multicolumn foreign key to be null unless all foreign key columns are null; if they are all null, the row is not required to have a match in the referenced table. MATCH SIMPLE allows any of the foreign key columns to be null; if any of them are null, the row is not required to have a match in the referenced table. MATCH PARTIAL is not yet implemented. (Of course, NOT NULL constraints can be applied to the referencing column(s) to prevent these cases from arising.)
I think currently it's viewed as an anti-feature, so it's not likely to land anytime soon.
As a workaround, you can create another table that just has (a,b)
CREATE TABLE baz (
a int,
b int,
PRIMARY KEY (a,b)
);
From here you can link both tables to it...
CREATE TABLE foo (
a int,
b int,
c int,
PRIMARY KEY (a,b,c),
FOREIGN KEY (a,b) REFERENCES baz
);
CREATE TABLE bar (
a int,
b int,
FOREIGN KEY (a,b) REFERENCES baz
);

What is the best way to enforce constraints across tables?

I often find myself running into situations like this one (which is contrived but illustrative of the problem):
CREATE TABLE customer (
id SERIAL PRIMARY KEY,
type TEXT
-- other columns...
);
CREATE TABLE product_order (
id SERIAL PRIMARY KEY,
customer_id INTEGER REFERENCES customer (id),
type TEXT REFERENCES customer (type), -- not actually legitimate
-- other columns...
CHECK (type = 'business')
);
Of course, the foreign key constraint on product_order.type doesn't work because customer.type is not UNIQUE or a primary key (and I can't use a CHECK CONSTRAINT on a column that only exists in another table). However, I would only like product_order entries for type = 'business' customers.
I could make customer.id and customer.type a composite primary key, but then any other tables that want to reference just customer.id must also reference customer.type unnecessarily.
What's the best approach in this situation?
EDIT: Forgot the foreign key constraint product_order.customer_id!
If you create a unique constraint on the customer.type you can reference it from the product_order table:
CREATE TABLE customer (
id SERIAL PRIMARY KEY,
type TEXT,
-- other columns...
constraint unique_cust_type unique (id, type) -- this makes the combination id/type "referencable"
);
CREATE TABLE product_order
(
id SERIAL PRIMARY KEY,
customer_id INTEGER,
type TEXT default 'business',
CHECK (type = 'business'),
foreign key (customer_id, type) references customer (id, type)
);
You could make a lookup table for types, and use the FKEY relationship to enforce
CREATE TABLE type (
id integer, PRIMARY KEY,
name TEXT
);
CREATE TABLE customer (
id SERIAL PRIMARY KEY,
type_id INTEGER, NOT NULL
-- other columns...
FOREIGN KEY (type_id) REFERENCES type(id)
);
CREATE TABLE product_order (
id SERIAL PRIMARY KEY,
type_id INTEGER, NOT NULL
-- other columns...
FOREIGN KEY (type_id) REFERENCES type(id)
);
You could access product_order as a view:
create view product_order_vw
as
select po.*
from product_order po
join customer c
on c.customerid = po.customerid
where c.type = 'business'
I think you need another type for the type. This sample is in MS SQL format:
CREATE TABLE TYPES (ctype varchar(10) NOT NULL PRIMARY KEY,
name varchar(50) not null
);
CREATE TABLE product_order (
id SERIAL PRIMARY KEY,
type varchar(10) NOT NULL REFERENCE TYPES (ctype) ,
....
);
So your product_order must have types defined in Types table. In your sample, you have only one entry 'business' but you can add as many as you want.

Deleting related rows from other tables without primary key

I need to delete the associated rows from the "assign" table when a crew member is deleted.
However, when I create the tables, I get the error:
ERROR: there is no unique constraint matching given keys for referenced table "assign"
It seems as though I need distinct values in the "assign" table, but the whole point is that I want to delete all associated information when a crew member is removed. How can I do this?
create table flight(
flight_num BIGSERIAL PRIMARY KEY,
source_city varchar,
dest_city varchar,
dep_time int,
arr_time int,
airfare int,
mileage int
);
create table crew (
id BIGSERIAL PRIMARY KEY,
name varchar,
salary int,
position varchar,
seniority int,
fly_hours int,
mgrid int,
FOREIGN KEY (id) REFERENCES assign(id) ON DELETE CASCADE
);
create table assign (
id int, # refers to a crew member id, not a row id
flight_num int
);
The foreign keys should be on the M-M join table, as so:
create table flight(
flight_num int PRIMARY KEY,
-- etc
);
create table crew (
crew_id int PRIMARY KEY,
-- etc
);
create table assign (
crew_id int,
flight_num int,
-- If the corresponding record in either of these FKs is deleted then
-- this record will be deleted.
FOREIGN KEY (crew_id) REFERENCES crew(crew_id) ON DELETE CASCADE,
FOREIGN KEY (flight_num) REFERENCES flight(flight_num) ON DELETE CASCADE,
-- M-M usually should have a PK over both foreign columns, arranged in order
-- of expected usage. A reverse covering index can be added if needed.
PRIMARY KEY (flight_num, crew_id)
);
In this case the FKs are allowed because they are over a unique columns (PKs of crew and flight). The original error is because there was no unique contraint on the FK target (assign.crew_id).