What is the best way to enforce constraints across tables? - sql

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.

Related

How to fix "ERROR: FK name length exceeds maximum allowed length(30)" in SQL Developer Data Modeller

I'm trying to turn the Logical Model of my Database into a DDL script, but I don't know how to fix this error in the DDL script or Data Modeller:-- ERROR: FK name length exceeds maximum allowed length(30)
It seems to be based on my junction-table's Primary Key, which is made up from two Foreign Keys from the two neighboring tables.
I have tried changing the names of the Primary Keys in the neighboring tables, but when I try to generate a NEW DDL script with Data Modeler, it still generates the old script.
Here's the sections of code that create the 3 tables and link them together:
CREATE TABLE items (
item_no NUMBER (8) NOT NULL,
"year" DATE,
price NUMBER (20,2)
);
ALTER TABLE items ADD CONSTRAINT items_pk PRIMARY KEY ( item_no );
CREATE TABLE purchase_order (
order_no NUMBER(8) NOT NULL,
quantity INTEGER,
item_description VARCHAR2(200),
unit_price NUMBER(20,2),
total NUMBER(20,2),
order_date DATE,
sales_person_code VARCHAR2(5) NOT NULL,
supplier_id NUMBER(3) NOT NULL
);
ALTER TABLE purchase_order ADD CONSTRAINT purchase_order_pk PRIMARY KEY ( order_no );
CREATE TABLE purchase_order_items (
purchase_order_order_no NUMBER(8) NOT NULL,
items_item_no NUMBER(8) NOT NULL
);
ALTER TABLE purchase_order_items ADD CONSTRAINT purchase_order_items_pk PRIMARY KEY ( items_item_no,
purchase_order_order_no );
ALTER TABLE purchase_order_items
ADD CONSTRAINT purchase_order_items_items_fk FOREIGN KEY ( items_item_no )
REFERENCES items ( item_no );
-- ERROR: FK name length exceeds maximum allowed length(30)
ALTER TABLE purchase_order_items
ADD CONSTRAINT purchase_order_items_purchase_order_fk FOREIGN KEY ( purchase_order_order_no )
REFERENCES purchase_order ( order_no );
ALTER TABLE purchase_order
ADD CONSTRAINT purchase_order_sales_person_fk FOREIGN KEY ( sales_person_code )
REFERENCES sales_person ( code );
ALTER TABLE purchase_order
ADD CONSTRAINT purchase_order_supplier_fk FOREIGN KEY ( supplier_id )
REFERENCES supplier ( id );
So I'm not sure exactly what FK name length is too long and what I need to change in the script to fix this error.
Oracle limits identifiers to 30 characters, so
purchase_order_items_purchase_order_fk needs to be shorted. Perhaps to something like poi_purchase_porder_fk.

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

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.

oracle - how to use foreign key constraints in object types

I have an error in the last line in Create table and i dont know how to use foreing key correctly.
CREATE TYPE CarType AS OBJECT(
price_id NUMBER,
quantity NUMBER
);
CREATE TABLE Cars(
carid NUMBER PRIMARY KEY,
carinfo CarType,
CONSTRAINT car_fk FOREIGN KEY(price_id) REFERENCES Prices(price_id)
);
CREATE TYPE CarType AS OBJECT(
price_id NUMBER,
quantity NUMBER
);
create table Prices
( cType CarType,
constraint pk_prices primary key (ctype.price_id) );
CREATE TABLE Cars(
carid NUMBER PRIMARY KEY,
carinfo cartype,
CONSTRAINT car_fk FOREIGN KEY(carinfo.price_id) REFERENCES prices(cType.price_id)
);
I assumed that you have Prices table that you didn't include in your question.

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).

Indirect UNIQUE constraint

I've had a following set of tables:
CREATE TABLE sellers (
id integer PRIMARY KEY,
....
);
CREATE TABLE buyers (
id integer PRIMARY KEY,
....
);
CREATE TABLE invoices (
id integer PRIMARY KEY,
number varchar(40),
buyer_id integer REFERENCES buyers(id),
seller_id integer REFERENCES sellers(id),
UNIQUE(seller_id, number),
....
);
The UNIQUE constraint is enforces the constraint that one seller can have only one invoice with given number.
I wanted to create a new table, mostly as a many-to-many relationship between buyers and sellers:
CREATE TABLE suppliers (
id integer PRIMARY KEY,
buyer_id integer REFERENCES buyers(id),
seller_id integer REFERENCES sellers(id),
....
);
As a normalization effort, i would like to change invoices to refer to suppliers, like this:
CREATE TABLE invoices (
id integer PRIMARY KEY,
number varchar(40),
supplier_id integer REFERENCES suppliers(id),
....
);
My question is: how can I replace the UNIQUE constraint?
Use a composite primary key for suppliers:
CREATE TABLE suppliers (
buyer_id integer REFERENCES buyers(id),
seller_id integer REFERENCES sellers(id),
...
PRIMARY KEY (buyer_id, seller_id),
...
);
and keep your invoices table intact, only changing the two foreign keys into one:
CREATE TABLE invoices (
id integer PRIMARY KEY,
number varchar(40),
buyer_id integer,
seller_id integer,
UNIQUE (seller_id, number),
FOREIGN KEY (buyer_id, seller_id)
REFERENCES suppliers (buyer_id, seller_id),
....
);
I guess that this design will be useful, if you plan to have more columns in the suppliers table that are related to the seller - buyer relationship. Otherwise, you could have a view that gathers data from the invoices table:
CREATE VIEW suppliers AS
SELECT DISTINCT buyer_id, seller_id
FROM invoices ;